1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chromeos/dbus/fake_cryptohome_client.h"
6
7#include "base/bind.h"
8#include "base/file_util.h"
9#include "base/location.h"
10#include "base/message_loop/message_loop.h"
11#include "base/path_service.h"
12#include "base/threading/worker_pool.h"
13#include "chromeos/chromeos_paths.h"
14#include "chromeos/dbus/cryptohome/key.pb.h"
15#include "chromeos/dbus/cryptohome/rpc.pb.h"
16#include "crypto/nss_util.h"
17#include "third_party/cros_system_api/dbus/service_constants.h"
18#include "third_party/protobuf/src/google/protobuf/io/coded_stream.h"
19#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h"
20#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream_impl_lite.h"
21
22namespace {
23
24// Helper to asynchronously write a file in the WorkerPool.
25void PersistFile(const base::FilePath& path, const std::string& content) {
26  base::WriteFile(path, content.data(), content.size());
27}
28
29}  // namespace
30
31namespace chromeos {
32
33FakeCryptohomeClient::FakeCryptohomeClient()
34    : service_is_available_(true),
35      async_call_id_(1),
36      tpm_is_ready_counter_(0),
37      unmount_result_(true),
38      system_salt_(GetStubSystemSalt()),
39      weak_ptr_factory_(this) {
40  base::FilePath cache_path;
41  locked_ = PathService::Get(chromeos::FILE_INSTALL_ATTRIBUTES, &cache_path) &&
42            base::PathExists(cache_path);
43}
44
45FakeCryptohomeClient::~FakeCryptohomeClient() {}
46
47void FakeCryptohomeClient::Init(dbus::Bus* bus) {
48}
49
50void FakeCryptohomeClient::SetAsyncCallStatusHandlers(
51    const AsyncCallStatusHandler& handler,
52    const AsyncCallStatusWithDataHandler& data_handler) {
53  async_call_status_handler_ = handler;
54  async_call_status_data_handler_ = data_handler;
55}
56
57void FakeCryptohomeClient::ResetAsyncCallStatusHandlers() {
58  async_call_status_handler_.Reset();
59  async_call_status_data_handler_.Reset();
60}
61
62void FakeCryptohomeClient::WaitForServiceToBeAvailable(
63    const WaitForServiceToBeAvailableCallback& callback) {
64  if (service_is_available_) {
65    base::MessageLoop::current()->PostTask(FROM_HERE,
66                                           base::Bind(callback, true));
67  } else {
68    pending_wait_for_service_to_be_available_callbacks_.push_back(callback);
69  }
70}
71
72void FakeCryptohomeClient::IsMounted(
73    const BoolDBusMethodCallback& callback) {
74  base::MessageLoop::current()->PostTask(
75      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true));
76}
77
78bool FakeCryptohomeClient::Unmount(bool* success) {
79  *success = unmount_result_;
80  return true;
81}
82
83void FakeCryptohomeClient::AsyncCheckKey(
84    const std::string& username,
85    const std::string& key,
86    const AsyncMethodCallback& callback) {
87  ReturnAsyncMethodResult(callback, false);
88}
89
90void FakeCryptohomeClient::AsyncMigrateKey(
91    const std::string& username,
92    const std::string& from_key,
93    const std::string& to_key,
94    const AsyncMethodCallback& callback) {
95  ReturnAsyncMethodResult(callback, false);
96}
97
98void FakeCryptohomeClient::AsyncRemove(
99    const std::string& username,
100    const AsyncMethodCallback& callback) {
101  ReturnAsyncMethodResult(callback, false);
102}
103
104void FakeCryptohomeClient::GetSystemSalt(
105    const GetSystemSaltCallback& callback) {
106  base::MessageLoop::current()->PostTask(
107      FROM_HERE,
108      base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, system_salt_));
109}
110
111void FakeCryptohomeClient::GetSanitizedUsername(
112    const std::string& username,
113    const StringDBusMethodCallback& callback) {
114  // Even for stub implementation we have to return different values so that
115  // multi-profiles would work.
116  std::string sanitized_username = GetStubSanitizedUsername(username);
117  base::MessageLoop::current()->PostTask(
118      FROM_HERE,
119      base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, sanitized_username));
120}
121
122std::string FakeCryptohomeClient::BlockingGetSanitizedUsername(
123    const std::string& username) {
124  return GetStubSanitizedUsername(username);
125}
126
127void FakeCryptohomeClient::AsyncMount(const std::string& username,
128                                          const std::string& key,
129                                          int flags,
130                                          const AsyncMethodCallback& callback) {
131  ReturnAsyncMethodResult(callback, false);
132}
133
134void FakeCryptohomeClient::AsyncAddKey(
135    const std::string& username,
136    const std::string& key,
137    const std::string& new_key,
138    const AsyncMethodCallback& callback) {
139  ReturnAsyncMethodResult(callback, false);
140}
141
142void FakeCryptohomeClient::AsyncMountGuest(
143    const AsyncMethodCallback& callback) {
144  ReturnAsyncMethodResult(callback, false);
145}
146
147void FakeCryptohomeClient::AsyncMountPublic(
148    const std::string& public_mount_id,
149    int flags,
150    const AsyncMethodCallback& callback) {
151  ReturnAsyncMethodResult(callback, false);
152}
153
154void FakeCryptohomeClient::TpmIsReady(
155    const BoolDBusMethodCallback& callback) {
156  base::MessageLoop::current()->PostTask(
157      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true));
158}
159
160void FakeCryptohomeClient::TpmIsEnabled(
161    const BoolDBusMethodCallback& callback) {
162  base::MessageLoop::current()->PostTask(
163      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true));
164}
165
166bool FakeCryptohomeClient::CallTpmIsEnabledAndBlock(bool* enabled) {
167  *enabled = true;
168  return true;
169}
170
171void FakeCryptohomeClient::TpmGetPassword(
172    const StringDBusMethodCallback& callback) {
173  const char kStubTpmPassword[] = "Stub-TPM-password";
174  base::MessageLoop::current()->PostTask(
175      FROM_HERE,
176      base::Bind(callback, DBUS_METHOD_CALL_SUCCESS,
177                 std::string(kStubTpmPassword)));
178}
179
180void FakeCryptohomeClient::TpmIsOwned(
181    const BoolDBusMethodCallback& callback) {
182  base::MessageLoop::current()->PostTask(
183      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true));
184}
185
186bool FakeCryptohomeClient::CallTpmIsOwnedAndBlock(bool* owned) {
187  *owned = true;
188  return true;
189}
190
191void FakeCryptohomeClient::TpmIsBeingOwned(
192    const BoolDBusMethodCallback& callback) {
193  base::MessageLoop::current()->PostTask(
194      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true));
195}
196
197bool FakeCryptohomeClient::CallTpmIsBeingOwnedAndBlock(bool* owning) {
198  *owning = true;
199  return true;
200}
201
202void FakeCryptohomeClient::TpmCanAttemptOwnership(
203    const VoidDBusMethodCallback& callback) {
204  base::MessageLoop::current()->PostTask(
205      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS));
206}
207
208void FakeCryptohomeClient::TpmClearStoredPassword(
209    const VoidDBusMethodCallback& callback) {
210  base::MessageLoop::current()->PostTask(
211      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS));
212}
213
214bool FakeCryptohomeClient::CallTpmClearStoredPasswordAndBlock() {
215  return true;
216}
217
218void FakeCryptohomeClient::Pkcs11IsTpmTokenReady(
219    const BoolDBusMethodCallback& callback) {
220  base::MessageLoop::current()->PostTask(
221      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true));
222}
223
224void FakeCryptohomeClient::Pkcs11GetTpmTokenInfo(
225    const Pkcs11GetTpmTokenInfoCallback& callback) {
226  const char kStubUserPin[] = "012345";
227  const int kStubSlot = 0;
228  base::MessageLoop::current()->PostTask(
229      FROM_HERE,
230      base::Bind(callback,
231                 DBUS_METHOD_CALL_SUCCESS,
232                 std::string(crypto::kTestTPMTokenName),
233                 std::string(kStubUserPin),
234                 kStubSlot));
235}
236
237void FakeCryptohomeClient::Pkcs11GetTpmTokenInfoForUser(
238    const std::string& username,
239    const Pkcs11GetTpmTokenInfoCallback& callback) {
240  Pkcs11GetTpmTokenInfo(callback);
241}
242
243bool FakeCryptohomeClient::InstallAttributesGet(const std::string& name,
244                                                    std::vector<uint8>* value,
245                                                    bool* successful) {
246  if (install_attrs_.find(name) != install_attrs_.end()) {
247    *value = install_attrs_[name];
248    *successful = true;
249  } else {
250    value->clear();
251    *successful = false;
252  }
253  return true;
254}
255
256bool FakeCryptohomeClient::InstallAttributesSet(
257    const std::string& name,
258    const std::vector<uint8>& value,
259    bool* successful) {
260  install_attrs_[name] = value;
261  *successful = true;
262  return true;
263}
264
265bool FakeCryptohomeClient::InstallAttributesFinalize(bool* successful) {
266  locked_ = true;
267  *successful = true;
268
269  // Persist the install attributes so that they can be reloaded if the
270  // browser is restarted. This is used for ease of development when device
271  // enrollment is required.
272  // The cryptohome::SerializedInstallAttributes protobuf lives in
273  // chrome/browser/chromeos, so it can't be used directly here; use the
274  // low-level protobuf API instead to just write the name-value pairs.
275  // The cache file is read by EnterpriseInstallAttributes::ReadCacheFile.
276  base::FilePath cache_path;
277  if (!PathService::Get(chromeos::FILE_INSTALL_ATTRIBUTES, &cache_path))
278    return false;
279
280  std::string result;
281  {
282    // |result| can be used only after the StringOutputStream goes out of
283    // scope.
284    google::protobuf::io::StringOutputStream result_stream(&result);
285    google::protobuf::io::CodedOutputStream result_output(&result_stream);
286
287    // These tags encode a variable-length value on the wire, which can be
288    // used to encode strings, bytes and messages. We only needs constants
289    // for tag numbers 1 and 2 (see install_attributes.proto).
290    const int kVarLengthTag1 = (1 << 3) | 0x2;
291    const int kVarLengthTag2 = (2 << 3) | 0x2;
292
293    typedef std::map<std::string, std::vector<uint8> >::const_iterator Iter;
294    for (Iter it = install_attrs_.begin(); it != install_attrs_.end(); ++it) {
295      std::string attr;
296      {
297        google::protobuf::io::StringOutputStream attr_stream(&attr);
298        google::protobuf::io::CodedOutputStream attr_output(&attr_stream);
299
300        attr_output.WriteVarint32(kVarLengthTag1);
301        attr_output.WriteVarint32(it->first.size());
302        attr_output.WriteString(it->first);
303        attr_output.WriteVarint32(kVarLengthTag2);
304        attr_output.WriteVarint32(it->second.size());
305        attr_output.WriteRaw(it->second.data(), it->second.size());
306      }
307
308      // Two CodedOutputStreams are needed because inner messages must be
309      // prefixed by their total length, which can't be easily computed before
310      // writing their tags and values.
311      result_output.WriteVarint32(kVarLengthTag2);
312      result_output.WriteVarint32(attr.size());
313      result_output.WriteRaw(attr.data(), attr.size());
314    }
315  }
316
317  base::WorkerPool::PostTask(
318      FROM_HERE, base::Bind(&PersistFile, cache_path, result), false);
319
320  return true;
321}
322
323void FakeCryptohomeClient::InstallAttributesIsReady(
324    const BoolDBusMethodCallback& callback) {
325  base::MessageLoop::current()->PostTask(
326      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true));
327}
328
329bool FakeCryptohomeClient::InstallAttributesIsInvalid(bool* is_invalid) {
330  *is_invalid = false;
331  return true;
332}
333
334bool FakeCryptohomeClient::InstallAttributesIsFirstInstall(
335    bool* is_first_install) {
336  *is_first_install = !locked_;
337  return true;
338}
339
340void FakeCryptohomeClient::TpmAttestationIsPrepared(
341    const BoolDBusMethodCallback& callback) {
342  base::MessageLoop::current()->PostTask(
343      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true));
344}
345
346void FakeCryptohomeClient::TpmAttestationIsEnrolled(
347    const BoolDBusMethodCallback& callback) {
348  base::MessageLoop::current()->PostTask(
349      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, true));
350}
351
352void FakeCryptohomeClient::AsyncTpmAttestationCreateEnrollRequest(
353    chromeos::attestation::PrivacyCAType pca_type,
354    const AsyncMethodCallback& callback) {
355  ReturnAsyncMethodResult(callback, true);
356}
357
358void FakeCryptohomeClient::AsyncTpmAttestationEnroll(
359    chromeos::attestation::PrivacyCAType pca_type,
360    const std::string& pca_response,
361    const AsyncMethodCallback& callback) {
362  ReturnAsyncMethodResult(callback, false);
363}
364
365void FakeCryptohomeClient::AsyncTpmAttestationCreateCertRequest(
366    chromeos::attestation::PrivacyCAType pca_type,
367    attestation::AttestationCertificateProfile certificate_profile,
368    const std::string& user_id,
369    const std::string& request_origin,
370    const AsyncMethodCallback& callback) {
371  ReturnAsyncMethodResult(callback, true);
372}
373
374void FakeCryptohomeClient::AsyncTpmAttestationFinishCertRequest(
375    const std::string& pca_response,
376    attestation::AttestationKeyType key_type,
377    const std::string& user_id,
378    const std::string& key_name,
379    const AsyncMethodCallback& callback) {
380  ReturnAsyncMethodResult(callback, true);
381}
382
383void FakeCryptohomeClient::TpmAttestationDoesKeyExist(
384    attestation::AttestationKeyType key_type,
385    const std::string& user_id,
386    const std::string& key_name,
387    const BoolDBusMethodCallback& callback) {
388  base::MessageLoop::current()->PostTask(
389      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, false));
390}
391
392void FakeCryptohomeClient::TpmAttestationGetCertificate(
393    attestation::AttestationKeyType key_type,
394    const std::string& user_id,
395    const std::string& key_name,
396    const DataMethodCallback& callback) {
397  base::MessageLoop::current()->PostTask(
398      FROM_HERE,
399      base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, false, std::string()));
400}
401
402void FakeCryptohomeClient::TpmAttestationGetPublicKey(
403    attestation::AttestationKeyType key_type,
404    const std::string& user_id,
405    const std::string& key_name,
406    const DataMethodCallback& callback) {
407  base::MessageLoop::current()->PostTask(
408      FROM_HERE,
409      base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, false, std::string()));
410}
411
412void FakeCryptohomeClient::TpmAttestationRegisterKey(
413    attestation::AttestationKeyType key_type,
414    const std::string& user_id,
415    const std::string& key_name,
416    const AsyncMethodCallback& callback) {
417  ReturnAsyncMethodResult(callback, true);
418}
419
420void FakeCryptohomeClient::TpmAttestationSignEnterpriseChallenge(
421    attestation::AttestationKeyType key_type,
422    const std::string& user_id,
423    const std::string& key_name,
424    const std::string& domain,
425    const std::string& device_id,
426    attestation::AttestationChallengeOptions options,
427    const std::string& challenge,
428    const AsyncMethodCallback& callback) {
429  ReturnAsyncMethodResult(callback, true);
430}
431
432void FakeCryptohomeClient::TpmAttestationSignSimpleChallenge(
433    attestation::AttestationKeyType key_type,
434    const std::string& user_id,
435    const std::string& key_name,
436    const std::string& challenge,
437    const AsyncMethodCallback& callback) {
438  ReturnAsyncMethodResult(callback, true);
439}
440
441void FakeCryptohomeClient::TpmAttestationGetKeyPayload(
442    attestation::AttestationKeyType key_type,
443    const std::string& user_id,
444    const std::string& key_name,
445    const DataMethodCallback& callback) {
446  base::MessageLoop::current()->PostTask(
447      FROM_HERE,
448      base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, false, std::string()));
449}
450
451void FakeCryptohomeClient::TpmAttestationSetKeyPayload(
452    attestation::AttestationKeyType key_type,
453    const std::string& user_id,
454    const std::string& key_name,
455    const std::string& payload,
456    const BoolDBusMethodCallback& callback) {
457  base::MessageLoop::current()->PostTask(
458      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, false));
459}
460
461void FakeCryptohomeClient::TpmAttestationDeleteKeys(
462    attestation::AttestationKeyType key_type,
463    const std::string& user_id,
464    const std::string& key_prefix,
465    const BoolDBusMethodCallback& callback) {
466  base::MessageLoop::current()->PostTask(
467      FROM_HERE, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS, false));
468}
469
470void FakeCryptohomeClient::CheckKeyEx(
471    const cryptohome::AccountIdentifier& id,
472    const cryptohome::AuthorizationRequest& auth,
473    const cryptohome::CheckKeyRequest& request,
474    const ProtobufMethodCallback& callback) {
475  ReturnProtobufMethodCallback(id.email(), callback);
476}
477
478void FakeCryptohomeClient::MountEx(
479    const cryptohome::AccountIdentifier& id,
480    const cryptohome::AuthorizationRequest& auth,
481    const cryptohome::MountRequest& request,
482    const ProtobufMethodCallback& callback) {
483  ReturnProtobufMethodCallback(id.email(), callback);
484}
485
486void FakeCryptohomeClient::AddKeyEx(
487    const cryptohome::AccountIdentifier& id,
488    const cryptohome::AuthorizationRequest& auth,
489    const cryptohome::AddKeyRequest& request,
490    const ProtobufMethodCallback& callback) {
491  ReturnProtobufMethodCallback(id.email(), callback);
492}
493
494void FakeCryptohomeClient::RemoveKeyEx(
495    const cryptohome::AccountIdentifier& id,
496    const cryptohome::AuthorizationRequest& auth,
497    const cryptohome::RemoveKeyRequest& request,
498    const ProtobufMethodCallback& callback) {
499  ReturnProtobufMethodCallback(id.email(), callback);
500}
501
502void FakeCryptohomeClient::UpdateKeyEx(
503    const cryptohome::AccountIdentifier& id,
504    const cryptohome::AuthorizationRequest& auth,
505    const cryptohome::UpdateKeyRequest& request,
506    const ProtobufMethodCallback& callback) {
507  ReturnProtobufMethodCallback(id.email(), callback);
508}
509
510void FakeCryptohomeClient::SetServiceIsAvailable(bool is_available) {
511  service_is_available_ = is_available;
512  if (is_available) {
513    std::vector<WaitForServiceToBeAvailableCallback> callbacks;
514    callbacks.swap(pending_wait_for_service_to_be_available_callbacks_);
515    for (size_t i = 0; i < callbacks.size(); ++i)
516      callbacks[i].Run(is_available);
517  }
518}
519
520// static
521std::vector<uint8> FakeCryptohomeClient::GetStubSystemSalt() {
522  const char kStubSystemSalt[] = "stub_system_salt";
523  return std::vector<uint8>(kStubSystemSalt,
524                            kStubSystemSalt + arraysize(kStubSystemSalt) - 1);
525}
526
527void FakeCryptohomeClient::ReturnProtobufMethodCallback(
528    const std::string& userid,
529    const ProtobufMethodCallback& callback) {
530  cryptohome::BaseReply reply;
531  reply.set_error(cryptohome::CRYPTOHOME_ERROR_NOT_SET);
532  cryptohome::MountReply* mount =
533      reply.MutableExtension(cryptohome::MountReply::reply);
534  mount->set_sanitized_username(GetStubSanitizedUsername(userid));
535  base::MessageLoop::current()->PostTask(
536      FROM_HERE,
537      base::Bind(callback,
538                 DBUS_METHOD_CALL_SUCCESS,
539                 true,
540                 reply));
541}
542
543void FakeCryptohomeClient::ReturnAsyncMethodResult(
544    const AsyncMethodCallback& callback,
545    bool returns_data) {
546  base::MessageLoop::current()->PostTask(
547      FROM_HERE,
548      base::Bind(&FakeCryptohomeClient::ReturnAsyncMethodResultInternal,
549                 weak_ptr_factory_.GetWeakPtr(),
550                 callback,
551                 returns_data));
552}
553
554void FakeCryptohomeClient::ReturnAsyncMethodResultInternal(
555    const AsyncMethodCallback& callback,
556    bool returns_data) {
557  callback.Run(async_call_id_);
558  if (!returns_data && !async_call_status_handler_.is_null()) {
559    base::MessageLoop::current()->PostTask(
560        FROM_HERE,
561        base::Bind(async_call_status_handler_,
562                   async_call_id_,
563                   true,
564                   cryptohome::MOUNT_ERROR_NONE));
565  } else if (returns_data && !async_call_status_data_handler_.is_null()) {
566    base::MessageLoop::current()->PostTask(
567        FROM_HERE,
568        base::Bind(async_call_status_data_handler_,
569                   async_call_id_,
570                   true,
571                   std::string()));
572  }
573  ++async_call_id_;
574}
575
576}  // namespace chromeos
577