1// Copyright (c) 2012 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/cryptohome/async_method_caller.h"
6
7#include "base/bind.h"
8#include "base/containers/hash_tables.h"
9#include "base/location.h"
10#include "base/message_loop/message_loop_proxy.h"
11#include "chromeos/dbus/cryptohome_client.h"
12#include "chromeos/dbus/dbus_thread_manager.h"
13
14using chromeos::DBusThreadManager;
15
16namespace cryptohome {
17
18namespace {
19
20AsyncMethodCaller* g_async_method_caller = NULL;
21
22// The implementation of AsyncMethodCaller
23class AsyncMethodCallerImpl : public AsyncMethodCaller {
24 public:
25  AsyncMethodCallerImpl() : weak_ptr_factory_(this) {
26    DBusThreadManager::Get()->GetCryptohomeClient()->SetAsyncCallStatusHandlers(
27        base::Bind(&AsyncMethodCallerImpl::HandleAsyncResponse,
28                   weak_ptr_factory_.GetWeakPtr()),
29        base::Bind(&AsyncMethodCallerImpl::HandleAsyncDataResponse,
30                   weak_ptr_factory_.GetWeakPtr()));
31  }
32
33  virtual ~AsyncMethodCallerImpl() {
34    DBusThreadManager::Get()->GetCryptohomeClient()->
35        ResetAsyncCallStatusHandlers();
36  }
37
38  virtual void AsyncCheckKey(const std::string& user_email,
39                             const std::string& passhash,
40                             Callback callback) OVERRIDE {
41    DBusThreadManager::Get()->GetCryptohomeClient()->
42        AsyncCheckKey(user_email, passhash, base::Bind(
43            &AsyncMethodCallerImpl::RegisterAsyncCallback,
44            weak_ptr_factory_.GetWeakPtr(),
45            callback,
46            "Couldn't initiate async check of user's key."));
47  }
48
49  virtual void AsyncMigrateKey(const std::string& user_email,
50                               const std::string& old_hash,
51                               const std::string& new_hash,
52                               Callback callback) OVERRIDE {
53    DBusThreadManager::Get()->GetCryptohomeClient()->
54        AsyncMigrateKey(user_email, old_hash, new_hash, base::Bind(
55            &AsyncMethodCallerImpl::RegisterAsyncCallback,
56            weak_ptr_factory_.GetWeakPtr(),
57            callback,
58            "Couldn't initiate aync migration of user's key"));
59  }
60
61  virtual void AsyncMount(const std::string& user_email,
62                          const std::string& passhash,
63                          int flags,
64                          Callback callback) OVERRIDE {
65    DBusThreadManager::Get()->GetCryptohomeClient()->
66        AsyncMount(user_email, passhash, flags, base::Bind(
67            &AsyncMethodCallerImpl::RegisterAsyncCallback,
68            weak_ptr_factory_.GetWeakPtr(),
69            callback,
70            "Couldn't initiate async mount of cryptohome."));
71  }
72
73  virtual void AsyncAddKey(const std::string& user_email,
74                           const std::string& passhash,
75                           const std::string& new_passhash,
76                           Callback callback) OVERRIDE {
77    DBusThreadManager::Get()->GetCryptohomeClient()->
78        AsyncAddKey(user_email, passhash, new_passhash, base::Bind(
79            &AsyncMethodCallerImpl::RegisterAsyncCallback,
80            weak_ptr_factory_.GetWeakPtr(),
81            callback,
82            "Couldn't initiate async key addition."));
83  }
84
85  virtual void AsyncMountGuest(Callback callback) OVERRIDE {
86    DBusThreadManager::Get()->GetCryptohomeClient()->
87        AsyncMountGuest(base::Bind(
88            &AsyncMethodCallerImpl::RegisterAsyncCallback,
89            weak_ptr_factory_.GetWeakPtr(),
90            callback,
91            "Couldn't initiate async mount of cryptohome."));
92  }
93
94  virtual void AsyncMountPublic(const std::string& public_mount_id,
95                                int flags,
96                                Callback callback) OVERRIDE {
97    DBusThreadManager::Get()->GetCryptohomeClient()->
98        AsyncMountPublic(public_mount_id, flags, base::Bind(
99            &AsyncMethodCallerImpl::RegisterAsyncCallback,
100            weak_ptr_factory_.GetWeakPtr(),
101            callback,
102            "Couldn't initiate async mount public of cryptohome."));
103  }
104
105  virtual void AsyncRemove(const std::string& user_email,
106                           Callback callback) OVERRIDE {
107    DBusThreadManager::Get()->GetCryptohomeClient()->
108        AsyncRemove(user_email, base::Bind(
109            &AsyncMethodCallerImpl::RegisterAsyncCallback,
110            weak_ptr_factory_.GetWeakPtr(),
111            callback,
112            "Couldn't initiate async removal of cryptohome."));
113  }
114
115  virtual void AsyncTpmAttestationCreateEnrollRequest(
116      chromeos::attestation::PrivacyCAType pca_type,
117      const DataCallback& callback) OVERRIDE {
118    DBusThreadManager::Get()->GetCryptohomeClient()->
119        AsyncTpmAttestationCreateEnrollRequest(pca_type, base::Bind(
120            &AsyncMethodCallerImpl::RegisterAsyncDataCallback,
121            weak_ptr_factory_.GetWeakPtr(),
122            callback,
123            "Couldn't initiate async attestation enroll request."));
124  }
125
126  virtual void AsyncTpmAttestationEnroll(
127      chromeos::attestation::PrivacyCAType pca_type,
128      const std::string& pca_response,
129      const Callback& callback) OVERRIDE {
130    DBusThreadManager::Get()->GetCryptohomeClient()->
131        AsyncTpmAttestationEnroll(pca_type, pca_response, base::Bind(
132            &AsyncMethodCallerImpl::RegisterAsyncCallback,
133            weak_ptr_factory_.GetWeakPtr(),
134            callback,
135            "Couldn't initiate async attestation enroll."));
136  }
137
138  virtual void AsyncTpmAttestationCreateCertRequest(
139      chromeos::attestation::PrivacyCAType pca_type,
140      chromeos::attestation::AttestationCertificateProfile certificate_profile,
141      const std::string& user_id,
142      const std::string& request_origin,
143      const DataCallback& callback) OVERRIDE {
144    DBusThreadManager::Get()->GetCryptohomeClient()->
145        AsyncTpmAttestationCreateCertRequest(
146            pca_type,
147            certificate_profile,
148            user_id,
149            request_origin,
150            base::Bind(&AsyncMethodCallerImpl::RegisterAsyncDataCallback,
151                       weak_ptr_factory_.GetWeakPtr(),
152                       callback,
153                       "Couldn't initiate async attestation cert request."));
154  }
155
156  virtual void AsyncTpmAttestationFinishCertRequest(
157      const std::string& pca_response,
158      chromeos::attestation::AttestationKeyType key_type,
159      const std::string& user_id,
160      const std::string& key_name,
161      const DataCallback& callback) OVERRIDE {
162    DBusThreadManager::Get()->GetCryptohomeClient()->
163        AsyncTpmAttestationFinishCertRequest(
164            pca_response,
165            key_type,
166            user_id,
167            key_name,
168            base::Bind(
169                &AsyncMethodCallerImpl::RegisterAsyncDataCallback,
170                weak_ptr_factory_.GetWeakPtr(),
171                callback,
172                "Couldn't initiate async attestation finish cert request."));
173  }
174
175  virtual void TpmAttestationRegisterKey(
176      chromeos::attestation::AttestationKeyType key_type,
177      const std::string& user_id,
178      const std::string& key_name,
179      const Callback& callback) OVERRIDE {
180    DBusThreadManager::Get()->GetCryptohomeClient()->
181        TpmAttestationRegisterKey(
182            key_type,
183            user_id,
184            key_name,
185            base::Bind(
186                &AsyncMethodCallerImpl::RegisterAsyncCallback,
187                weak_ptr_factory_.GetWeakPtr(),
188                callback,
189                "Couldn't initiate async attestation register key."));
190  }
191
192  virtual void TpmAttestationSignEnterpriseChallenge(
193      chromeos::attestation::AttestationKeyType key_type,
194      const std::string& user_id,
195      const std::string& key_name,
196      const std::string& domain,
197      const std::string& device_id,
198      chromeos::attestation::AttestationChallengeOptions options,
199      const std::string& challenge,
200      const DataCallback& callback) OVERRIDE {
201    DBusThreadManager::Get()->GetCryptohomeClient()->
202        TpmAttestationSignEnterpriseChallenge(
203            key_type,
204            user_id,
205            key_name,
206            domain,
207            device_id,
208            options,
209            challenge,
210            base::Bind(
211                &AsyncMethodCallerImpl::RegisterAsyncDataCallback,
212                weak_ptr_factory_.GetWeakPtr(),
213                callback,
214                "Couldn't initiate async attestation enterprise challenge."));
215  }
216
217  virtual void TpmAttestationSignSimpleChallenge(
218      chromeos::attestation::AttestationKeyType key_type,
219      const std::string& user_id,
220      const std::string& key_name,
221      const std::string& challenge,
222      const DataCallback& callback) OVERRIDE {
223    DBusThreadManager::Get()->GetCryptohomeClient()->
224        TpmAttestationSignSimpleChallenge(
225            key_type,
226            user_id,
227            key_name,
228            challenge,
229            base::Bind(
230                &AsyncMethodCallerImpl::RegisterAsyncDataCallback,
231                weak_ptr_factory_.GetWeakPtr(),
232                callback,
233                "Couldn't initiate async attestation simple challenge."));
234  }
235
236  virtual void AsyncGetSanitizedUsername(
237      const std::string& user,
238      const DataCallback& callback) OVERRIDE {
239    DBusThreadManager::Get()->GetCryptohomeClient()->
240        GetSanitizedUsername(user,
241        base::Bind(
242            &AsyncMethodCallerImpl::GetSanitizedUsernameCallback,
243            weak_ptr_factory_.GetWeakPtr(),
244            callback));
245  }
246
247  virtual void GetSanitizedUsernameCallback(
248      const DataCallback& callback,
249      const chromeos::DBusMethodCallStatus call_status,
250      const std::string& result) {
251    callback.Run(true, result);
252  }
253
254 private:
255  struct CallbackElement {
256    CallbackElement() {}
257    explicit CallbackElement(const AsyncMethodCaller::Callback& callback)
258        : callback(callback),
259          proxy(base::MessageLoopProxy::current()) {
260    }
261    AsyncMethodCaller::Callback callback;
262    scoped_refptr<base::MessageLoopProxy> proxy;
263  };
264
265  struct DataCallbackElement {
266    DataCallbackElement() {}
267    explicit DataCallbackElement(
268        const AsyncMethodCaller::DataCallback& callback)
269        : data_callback(callback),
270          proxy(base::MessageLoopProxy::current()) {
271    }
272    AsyncMethodCaller::DataCallback data_callback;
273    scoped_refptr<base::MessageLoopProxy> proxy;
274  };
275
276  typedef base::hash_map<int, CallbackElement> CallbackMap;
277  typedef base::hash_map<int, DataCallbackElement> DataCallbackMap;
278
279  // Handles the response for async calls.
280  // Below is described how async calls work.
281  // 1. CryptohomeClient::AsyncXXX returns "async ID".
282  // 2. RegisterAsyncCallback registers the "async ID" with the user-provided
283  //    callback.
284  // 3. Cryptohome will return the result asynchronously as a signal with
285  //    "async ID"
286  // 4. "HandleAsyncResponse" handles the result signal and call the registered
287  //    callback associated with the "async ID".
288  void HandleAsyncResponse(int async_id, bool return_status, int return_code) {
289    const CallbackMap::iterator it = callback_map_.find(async_id);
290    if (it == callback_map_.end()) {
291      LOG(ERROR) << "Received signal for unknown async_id " << async_id;
292      return;
293    }
294    it->second.proxy->PostTask(FROM_HERE,
295        base::Bind(it->second.callback,
296                   return_status,
297                   static_cast<MountError>(return_code)));
298    callback_map_.erase(it);
299  }
300
301  // Similar to HandleAsyncResponse but for signals with a raw data payload.
302  void HandleAsyncDataResponse(int async_id,
303                               bool return_status,
304                               const std::string& return_data) {
305    const DataCallbackMap::iterator it = data_callback_map_.find(async_id);
306    if (it == data_callback_map_.end()) {
307      LOG(ERROR) << "Received signal for unknown async_id " << async_id;
308      return;
309    }
310    it->second.proxy->PostTask(FROM_HERE,
311        base::Bind(it->second.data_callback, return_status, return_data));
312    data_callback_map_.erase(it);
313  }
314
315  // Registers a callback which is called when the result for AsyncXXX is ready.
316  void RegisterAsyncCallback(
317      Callback callback, const char* error, int async_id) {
318    if (async_id == 0) {
319      LOG(ERROR) << error;
320      return;
321    }
322    VLOG(1) << "Adding handler for " << async_id;
323    DCHECK_EQ(callback_map_.count(async_id), 0U);
324    DCHECK_EQ(data_callback_map_.count(async_id), 0U);
325    callback_map_[async_id] = CallbackElement(callback);
326  }
327
328  // Registers a callback which is called when the result for AsyncXXX is ready.
329  void RegisterAsyncDataCallback(
330      DataCallback callback, const char* error, int async_id) {
331    if (async_id == 0) {
332      LOG(ERROR) << error;
333      return;
334    }
335    VLOG(1) << "Adding handler for " << async_id;
336    DCHECK_EQ(callback_map_.count(async_id), 0U);
337    DCHECK_EQ(data_callback_map_.count(async_id), 0U);
338    data_callback_map_[async_id] = DataCallbackElement(callback);
339  }
340
341  base::WeakPtrFactory<AsyncMethodCallerImpl> weak_ptr_factory_;
342  CallbackMap callback_map_;
343  DataCallbackMap data_callback_map_;
344
345  DISALLOW_COPY_AND_ASSIGN(AsyncMethodCallerImpl);
346};
347
348}  // namespace
349
350// static
351void AsyncMethodCaller::Initialize() {
352  if (g_async_method_caller) {
353    LOG(WARNING) << "AsyncMethodCaller was already initialized";
354    return;
355  }
356  g_async_method_caller = new AsyncMethodCallerImpl();
357  VLOG(1) << "AsyncMethodCaller initialized";
358}
359
360// static
361void AsyncMethodCaller::InitializeForTesting(
362    AsyncMethodCaller* async_method_caller) {
363  if (g_async_method_caller) {
364    LOG(WARNING) << "AsyncMethodCaller was already initialized";
365    return;
366  }
367  g_async_method_caller = async_method_caller;
368  VLOG(1) << "AsyncMethodCaller initialized";
369}
370
371// static
372void AsyncMethodCaller::Shutdown() {
373  if (!g_async_method_caller) {
374    LOG(WARNING) << "AsyncMethodCaller::Shutdown() called with NULL manager";
375    return;
376  }
377  delete g_async_method_caller;
378  g_async_method_caller = NULL;
379  VLOG(1) << "AsyncMethodCaller Shutdown completed";
380}
381
382// static
383AsyncMethodCaller* AsyncMethodCaller::GetInstance() {
384  return g_async_method_caller;
385}
386
387}  // namespace cryptohome
388