1// Copyright 2014 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/homedir_methods.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "chromeos/dbus/cryptohome/key.pb.h"
10#include "chromeos/dbus/cryptohome/rpc.pb.h"
11#include "chromeos/dbus/cryptohome_client.h"
12#include "chromeos/dbus/dbus_thread_manager.h"
13
14#if defined(USE_SYSTEM_PROTOBUF)
15#include <google/protobuf/repeated_field.h>
16#else
17#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
18#endif
19
20using chromeos::DBusThreadManager;
21using google::protobuf::RepeatedPtrField;
22
23namespace cryptohome {
24
25namespace {
26
27HomedirMethods* g_homedir_methods = NULL;
28
29void FillKeyProtobuf(const KeyDefinition& key_def, Key* key) {
30  key->set_secret(key_def.secret);
31  KeyData* data = key->mutable_data();
32  DCHECK_EQ(KeyDefinition::TYPE_PASSWORD, key_def.type);
33  data->set_type(KeyData::KEY_TYPE_PASSWORD);
34  data->set_label(key_def.label);
35
36  if (key_def.revision > 0)
37    data->set_revision(key_def.revision);
38
39  if (key_def.privileges != 0) {
40    KeyPrivileges* privileges = data->mutable_privileges();
41    privileges->set_mount(key_def.privileges & PRIV_MOUNT);
42    privileges->set_add(key_def.privileges & PRIV_ADD);
43    privileges->set_remove(key_def.privileges & PRIV_REMOVE);
44    privileges->set_update(key_def.privileges & PRIV_MIGRATE);
45    privileges->set_authorized_update(key_def.privileges &
46                                      PRIV_AUTHORIZED_UPDATE);
47  }
48
49  for (std::vector<KeyDefinition::AuthorizationData>::const_iterator auth_it =
50          key_def.authorization_data.begin();
51       auth_it != key_def.authorization_data.end(); ++auth_it) {
52    KeyAuthorizationData* auth_data = data->add_authorization_data();
53    switch (auth_it->type) {
54      case KeyDefinition::AuthorizationData::TYPE_HMACSHA256:
55        auth_data->set_type(
56            KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_HMACSHA256);
57        break;
58      case KeyDefinition::AuthorizationData::TYPE_AES256CBC_HMACSHA256:
59        auth_data->set_type(
60            KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_AES256CBC_HMACSHA256);
61        break;
62      default:
63        NOTREACHED();
64        break;
65    }
66
67    for (std::vector<KeyDefinition::AuthorizationData::Secret>::const_iterator
68             secret_it = auth_it->secrets.begin();
69         secret_it != auth_it->secrets.end(); ++secret_it) {
70      KeyAuthorizationSecret* secret = auth_data->add_secrets();
71      secret->mutable_usage()->set_encrypt(secret_it->encrypt);
72      secret->mutable_usage()->set_sign(secret_it->sign);
73      if (!secret_it->symmetric_key.empty())
74        secret->set_symmetric_key(secret_it->symmetric_key);
75      if (!secret_it->public_key.empty())
76        secret->set_public_key(secret_it->public_key);
77      secret->set_wrapped(secret_it->wrapped);
78    }
79  }
80
81  for (std::vector<KeyDefinition::ProviderData>::const_iterator it =
82           key_def.provider_data.begin(); it != key_def.provider_data.end();
83       ++it) {
84    KeyProviderData::Entry* entry =
85        data->mutable_provider_data()->add_entry();
86    entry->set_name(it->name);
87    if (it->number)
88      entry->set_number(*it->number);
89    if (it->bytes)
90      entry->set_bytes(*it->bytes);
91  }
92}
93
94// Fill identification protobuffer.
95void FillIdentificationProtobuf(const Identification& id,
96                                cryptohome::AccountIdentifier* id_proto) {
97  id_proto->set_email(id.user_id);
98}
99
100// Fill authorization protobuffer.
101void FillAuthorizationProtobuf(const Authorization& auth,
102                               cryptohome::AuthorizationRequest* auth_proto) {
103  Key* key = auth_proto->mutable_key();
104  if (!auth.label.empty()) {
105    key->mutable_data()->set_label(auth.label);
106  }
107  key->set_secret(auth.key);
108}
109
110void ParseAuthorizationDataProtobuf(
111    const KeyAuthorizationData& authorization_data_proto,
112    KeyDefinition::AuthorizationData* authorization_data) {
113  switch (authorization_data_proto.type()) {
114    case KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_HMACSHA256:
115      authorization_data->type =
116          KeyDefinition::AuthorizationData::TYPE_HMACSHA256;
117      break;
118    case KeyAuthorizationData::KEY_AUTHORIZATION_TYPE_AES256CBC_HMACSHA256:
119      authorization_data->type =
120          KeyDefinition::AuthorizationData::TYPE_AES256CBC_HMACSHA256;
121      break;
122    default:
123      NOTREACHED();
124      return;
125  }
126
127  for (RepeatedPtrField<KeyAuthorizationSecret>::const_iterator it =
128          authorization_data_proto.secrets().begin();
129       it != authorization_data_proto.secrets().end(); ++it) {
130    authorization_data->secrets.push_back(
131        KeyDefinition::AuthorizationData::Secret(it->usage().encrypt(),
132                                                 it->usage().sign(),
133                                                 it->symmetric_key(),
134                                                 it->public_key(),
135                                                 it->wrapped()));
136  }
137}
138
139MountError MapError(CryptohomeErrorCode code) {
140  switch (code) {
141    case CRYPTOHOME_ERROR_NOT_SET:
142      return MOUNT_ERROR_NONE;
143    case CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND:
144      return MOUNT_ERROR_USER_DOES_NOT_EXIST;
145    case CRYPTOHOME_ERROR_NOT_IMPLEMENTED:
146    case CRYPTOHOME_ERROR_MOUNT_FATAL:
147    case CRYPTOHOME_ERROR_KEY_QUOTA_EXCEEDED:
148    case CRYPTOHOME_ERROR_BACKING_STORE_FAILURE:
149      return MOUNT_ERROR_FATAL;
150    case CRYPTOHOME_ERROR_AUTHORIZATION_KEY_NOT_FOUND:
151    case CRYPTOHOME_ERROR_KEY_NOT_FOUND:
152    case CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED:
153      return MOUNT_ERROR_KEY_FAILURE;
154    case CRYPTOHOME_ERROR_TPM_COMM_ERROR:
155      return MOUNT_ERROR_TPM_COMM_ERROR;
156    case CRYPTOHOME_ERROR_TPM_DEFEND_LOCK:
157      return MOUNT_ERROR_TPM_DEFEND_LOCK;
158    case CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY:
159      return MOUNT_ERROR_MOUNT_POINT_BUSY;
160    case CRYPTOHOME_ERROR_TPM_NEEDS_REBOOT:
161      return MOUNT_ERROR_TPM_NEEDS_REBOOT;
162    case CRYPTOHOME_ERROR_AUTHORIZATION_KEY_DENIED:
163    case CRYPTOHOME_ERROR_KEY_LABEL_EXISTS:
164    case CRYPTOHOME_ERROR_UPDATE_SIGNATURE_INVALID:
165      return MOUNT_ERROR_KEY_FAILURE;
166    default:
167      NOTREACHED();
168      return MOUNT_ERROR_FATAL;
169  }
170}
171
172// The implementation of HomedirMethods
173class HomedirMethodsImpl : public HomedirMethods {
174 public:
175  HomedirMethodsImpl() : weak_ptr_factory_(this) {}
176
177  virtual ~HomedirMethodsImpl() {}
178
179  virtual void GetKeyDataEx(const Identification& id,
180                            const std::string& label,
181                            const GetKeyDataCallback& callback) OVERRIDE {
182    cryptohome::AccountIdentifier id_proto;
183    cryptohome::AuthorizationRequest kEmptyAuthProto;
184    cryptohome::GetKeyDataRequest request;
185
186    FillIdentificationProtobuf(id, &id_proto);
187    request.mutable_key()->mutable_data()->set_label(label);
188
189    DBusThreadManager::Get()->GetCryptohomeClient()->GetKeyDataEx(
190        id_proto,
191        kEmptyAuthProto,
192        request,
193        base::Bind(&HomedirMethodsImpl::OnGetKeyDataExCallback,
194                   weak_ptr_factory_.GetWeakPtr(),
195                   callback));
196  }
197
198  virtual void CheckKeyEx(const Identification& id,
199                          const Authorization& auth,
200                          const Callback& callback) OVERRIDE {
201    cryptohome::AccountIdentifier id_proto;
202    cryptohome::AuthorizationRequest auth_proto;
203    cryptohome::CheckKeyRequest request;
204
205    FillIdentificationProtobuf(id, &id_proto);
206    FillAuthorizationProtobuf(auth, &auth_proto);
207
208    DBusThreadManager::Get()->GetCryptohomeClient()->CheckKeyEx(
209        id_proto,
210        auth_proto,
211        request,
212        base::Bind(&HomedirMethodsImpl::OnBaseReplyCallback,
213                   weak_ptr_factory_.GetWeakPtr(),
214                   callback));
215  }
216
217  virtual void MountEx(const Identification& id,
218                       const Authorization& auth,
219                       const MountParameters& request,
220                       const MountCallback& callback) OVERRIDE {
221    cryptohome::AccountIdentifier id_proto;
222    cryptohome::AuthorizationRequest auth_proto;
223    cryptohome::MountRequest request_proto;
224
225    FillIdentificationProtobuf(id, &id_proto);
226    FillAuthorizationProtobuf(auth, &auth_proto);
227
228    if (request.ephemeral)
229      request_proto.set_require_ephemeral(true);
230
231    if (!request.create_keys.empty()) {
232      CreateRequest* create = request_proto.mutable_create();
233      for (size_t i = 0; i < request.create_keys.size(); ++i)
234        FillKeyProtobuf(request.create_keys[i], create->add_keys());
235    }
236
237    DBusThreadManager::Get()->GetCryptohomeClient()->MountEx(
238        id_proto,
239        auth_proto,
240        request_proto,
241        base::Bind(&HomedirMethodsImpl::OnMountExCallback,
242                   weak_ptr_factory_.GetWeakPtr(),
243                   callback));
244  }
245
246  virtual void AddKeyEx(const Identification& id,
247                        const Authorization& auth,
248                        const KeyDefinition& new_key,
249                        bool clobber_if_exists,
250                        const Callback& callback) OVERRIDE {
251    cryptohome::AccountIdentifier id_proto;
252    cryptohome::AuthorizationRequest auth_proto;
253    cryptohome::AddKeyRequest request;
254
255    FillIdentificationProtobuf(id, &id_proto);
256    FillAuthorizationProtobuf(auth, &auth_proto);
257    FillKeyProtobuf(new_key, request.mutable_key());
258    request.set_clobber_if_exists(clobber_if_exists);
259
260    DBusThreadManager::Get()->GetCryptohomeClient()->AddKeyEx(
261        id_proto,
262        auth_proto,
263        request,
264        base::Bind(&HomedirMethodsImpl::OnBaseReplyCallback,
265                   weak_ptr_factory_.GetWeakPtr(),
266                   callback));
267  }
268
269  virtual void RemoveKeyEx(const Identification& id,
270                           const Authorization& auth,
271                           const std::string& label,
272                           const Callback& callback) OVERRIDE {
273    cryptohome::AccountIdentifier id_proto;
274    cryptohome::AuthorizationRequest auth_proto;
275    cryptohome::RemoveKeyRequest request;
276
277    FillIdentificationProtobuf(id, &id_proto);
278    FillAuthorizationProtobuf(auth, &auth_proto);
279    request.mutable_key()->mutable_data()->set_label(label);
280
281    DBusThreadManager::Get()->GetCryptohomeClient()->RemoveKeyEx(
282        id_proto,
283        auth_proto,
284        request,
285        base::Bind(&HomedirMethodsImpl::OnBaseReplyCallback,
286                   weak_ptr_factory_.GetWeakPtr(),
287                   callback));
288  }
289
290  virtual void UpdateKeyEx(const Identification& id,
291                           const Authorization& auth,
292                           const KeyDefinition& new_key,
293                           const std::string& signature,
294                           const Callback& callback) OVERRIDE {
295    cryptohome::AccountIdentifier id_proto;
296    cryptohome::AuthorizationRequest auth_proto;
297    cryptohome::UpdateKeyRequest pb_update_key;
298
299    FillIdentificationProtobuf(id, &id_proto);
300    FillAuthorizationProtobuf(auth, &auth_proto);
301    FillKeyProtobuf(new_key, pb_update_key.mutable_changes());
302    pb_update_key.set_authorization_signature(signature);
303
304    DBusThreadManager::Get()->GetCryptohomeClient()->UpdateKeyEx(
305        id_proto,
306        auth_proto,
307        pb_update_key,
308        base::Bind(&HomedirMethodsImpl::OnBaseReplyCallback,
309                   weak_ptr_factory_.GetWeakPtr(),
310                   callback));
311  }
312
313 private:
314  void OnGetKeyDataExCallback(const GetKeyDataCallback& callback,
315                              chromeos::DBusMethodCallStatus call_status,
316                              bool result,
317                              const BaseReply& reply) {
318    if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS) {
319      callback.Run(false, MOUNT_ERROR_FATAL, std::vector<KeyDefinition>());
320      return;
321    }
322    if (reply.has_error()) {
323      if (reply.error() != CRYPTOHOME_ERROR_NOT_SET) {
324        callback.Run(false,
325                     MapError(reply.error()),
326                     std::vector<KeyDefinition>());
327        return;
328      }
329    }
330
331    if (!reply.HasExtension(GetKeyDataReply::reply)) {
332      callback.Run(false, MOUNT_ERROR_FATAL, std::vector<KeyDefinition>());
333      return;
334    }
335
336    // Extract the contents of the |KeyData| protos returned.
337    const RepeatedPtrField<KeyData>& key_data =
338        reply.GetExtension(GetKeyDataReply::reply).key_data();
339    std::vector<KeyDefinition> key_definitions;
340    for (RepeatedPtrField<KeyData>::const_iterator it = key_data.begin();
341         it != key_data.end(); ++it) {
342
343      // Extract |type|, |label| and |revision|.
344      DCHECK_EQ(KeyData::KEY_TYPE_PASSWORD, it->type());
345      key_definitions.push_back(KeyDefinition(std::string() /* secret */,
346                                              it->label(),
347                                              0 /* privileges */));
348      KeyDefinition& key_definition = key_definitions.back();
349      key_definition.revision = it->revision();
350
351      // Extract |privileges|.
352      const KeyPrivileges& privileges = it->privileges();
353      if (privileges.mount())
354        key_definition.privileges |= PRIV_MOUNT;
355      if (privileges.add())
356        key_definition.privileges |= PRIV_ADD;
357      if (privileges.remove())
358        key_definition.privileges |= PRIV_REMOVE;
359      if (privileges.update())
360        key_definition.privileges |= PRIV_MIGRATE;
361      if (privileges.authorized_update())
362        key_definition.privileges |= PRIV_AUTHORIZED_UPDATE;
363
364      // Extract |authorization_data|.
365      for (RepeatedPtrField<KeyAuthorizationData>::const_iterator auth_it =
366               it->authorization_data().begin();
367           auth_it != it->authorization_data().end(); ++auth_it) {
368        key_definition.authorization_data.push_back(
369            KeyDefinition::AuthorizationData());
370        ParseAuthorizationDataProtobuf(
371            *auth_it,
372            &key_definition.authorization_data.back());
373      }
374
375      // Extract |provider_data|.
376      for (RepeatedPtrField<KeyProviderData::Entry>::const_iterator
377              provider_data_it = it->provider_data().entry().begin();
378           provider_data_it != it->provider_data().entry().end();
379           ++provider_data_it) {
380        // Extract |name|.
381        key_definition.provider_data.push_back(
382            KeyDefinition::ProviderData(provider_data_it->name()));
383        KeyDefinition::ProviderData& provider_data =
384            key_definition.provider_data.back();
385
386        int data_items = 0;
387
388        // Extract |number|.
389        if (provider_data_it->has_number()) {
390          provider_data.number.reset(new int64(provider_data_it->number()));
391          ++data_items;
392        }
393
394        // Extract |bytes|.
395        if (provider_data_it->has_bytes()) {
396          provider_data.bytes.reset(
397              new std::string(provider_data_it->bytes()));
398          ++data_items;
399        }
400
401        DCHECK_EQ(1, data_items);
402      }
403    }
404
405    callback.Run(true, MOUNT_ERROR_NONE, key_definitions);
406  }
407
408  void OnMountExCallback(const MountCallback& callback,
409                         chromeos::DBusMethodCallStatus call_status,
410                         bool result,
411                         const BaseReply& reply) {
412    if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS) {
413      callback.Run(false, MOUNT_ERROR_FATAL, std::string());
414      return;
415    }
416    if (reply.has_error()) {
417      if (reply.error() != CRYPTOHOME_ERROR_NOT_SET) {
418        callback.Run(false, MapError(reply.error()), std::string());
419        return;
420      }
421    }
422    if (!reply.HasExtension(MountReply::reply)) {
423      callback.Run(false, MOUNT_ERROR_FATAL, std::string());
424      return;
425    }
426
427    std::string mount_hash;
428    mount_hash = reply.GetExtension(MountReply::reply).sanitized_username();
429    callback.Run(true, MOUNT_ERROR_NONE, mount_hash);
430  }
431
432  void OnBaseReplyCallback(const Callback& callback,
433                           chromeos::DBusMethodCallStatus call_status,
434                           bool result,
435                           const BaseReply& reply) {
436    if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS) {
437      callback.Run(false, MOUNT_ERROR_FATAL);
438      return;
439    }
440    if (reply.has_error()) {
441      if (reply.error() != CRYPTOHOME_ERROR_NOT_SET) {
442        callback.Run(false, MapError(reply.error()));
443        return;
444      }
445    }
446    callback.Run(true, MOUNT_ERROR_NONE);
447  }
448
449  base::WeakPtrFactory<HomedirMethodsImpl> weak_ptr_factory_;
450
451  DISALLOW_COPY_AND_ASSIGN(HomedirMethodsImpl);
452};
453
454}  // namespace
455
456// static
457void HomedirMethods::Initialize() {
458  if (g_homedir_methods) {
459    LOG(WARNING) << "HomedirMethods was already initialized";
460    return;
461  }
462  g_homedir_methods = new HomedirMethodsImpl();
463  VLOG(1) << "HomedirMethods initialized";
464}
465
466// static
467void HomedirMethods::InitializeForTesting(HomedirMethods* homedir_methods) {
468  if (g_homedir_methods) {
469    LOG(WARNING) << "HomedirMethods was already initialized";
470    return;
471  }
472  g_homedir_methods = homedir_methods;
473  VLOG(1) << "HomedirMethods initialized";
474}
475
476// static
477void HomedirMethods::Shutdown() {
478  if (!g_homedir_methods) {
479    LOG(WARNING) << "AsyncMethodCaller::Shutdown() called with NULL manager";
480    return;
481  }
482  delete g_homedir_methods;
483  g_homedir_methods = NULL;
484  VLOG(1) << "HomedirMethods Shutdown completed";
485}
486
487// static
488HomedirMethods* HomedirMethods::GetInstance() { return g_homedir_methods; }
489
490}  // namespace cryptohome
491