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/login/auth/extended_authenticator_impl.h"
6
7#include "base/bind.h"
8#include "base/strings/string_number_conversions.h"
9#include "base/strings/string_util.h"
10#include "chromeos/cryptohome/async_method_caller.h"
11#include "chromeos/cryptohome/cryptohome_parameters.h"
12#include "chromeos/cryptohome/homedir_methods.h"
13#include "chromeos/cryptohome/system_salt_getter.h"
14#include "chromeos/dbus/cryptohome_client.h"
15#include "chromeos/dbus/dbus_thread_manager.h"
16#include "chromeos/login/auth/auth_status_consumer.h"
17#include "chromeos/login/auth/key.h"
18#include "chromeos/login/auth/user_context.h"
19#include "chromeos/login_event_recorder.h"
20#include "crypto/sha2.h"
21#include "google_apis/gaia/gaia_auth_util.h"
22#include "third_party/cros_system_api/dbus/service_constants.h"
23
24namespace chromeos {
25
26namespace {
27
28void RecordStartMarker(const std::string& marker) {
29  std::string full_marker = "Cryptohome-";
30  full_marker.append(marker);
31  full_marker.append("-Start");
32  chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(full_marker, false);
33}
34
35void RecordEndMarker(const std::string& marker) {
36  std::string full_marker = "Cryptohome-";
37  full_marker.append(marker);
38  full_marker.append("-End");
39  chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(full_marker, false);
40}
41
42}  // namespace
43
44ExtendedAuthenticatorImpl::ExtendedAuthenticatorImpl(
45    NewAuthStatusConsumer* consumer)
46    : salt_obtained_(false), consumer_(consumer), old_consumer_(NULL) {
47  SystemSaltGetter::Get()->GetSystemSalt(
48      base::Bind(&ExtendedAuthenticatorImpl::OnSaltObtained, this));
49}
50
51ExtendedAuthenticatorImpl::ExtendedAuthenticatorImpl(
52    AuthStatusConsumer* consumer)
53    : salt_obtained_(false), consumer_(NULL), old_consumer_(consumer) {
54  SystemSaltGetter::Get()->GetSystemSalt(
55      base::Bind(&ExtendedAuthenticatorImpl::OnSaltObtained, this));
56}
57
58void ExtendedAuthenticatorImpl::SetConsumer(AuthStatusConsumer* consumer) {
59  old_consumer_ = consumer;
60}
61
62void ExtendedAuthenticatorImpl::AuthenticateToMount(
63    const UserContext& context,
64    const ResultCallback& success_callback) {
65  TransformKeyIfNeeded(
66      context,
67      base::Bind(&ExtendedAuthenticatorImpl::DoAuthenticateToMount,
68                 this,
69                 success_callback));
70}
71
72void ExtendedAuthenticatorImpl::AuthenticateToCheck(
73    const UserContext& context,
74    const base::Closure& success_callback) {
75  TransformKeyIfNeeded(
76      context,
77      base::Bind(&ExtendedAuthenticatorImpl::DoAuthenticateToCheck,
78                 this,
79                 success_callback));
80}
81
82void ExtendedAuthenticatorImpl::CreateMount(
83    const std::string& user_id,
84    const std::vector<cryptohome::KeyDefinition>& keys,
85    const ResultCallback& success_callback) {
86  RecordStartMarker("MountEx");
87
88  std::string canonicalized = gaia::CanonicalizeEmail(user_id);
89  cryptohome::Identification id(canonicalized);
90  cryptohome::Authorization auth(keys.front());
91  cryptohome::MountParameters mount(false);
92  for (size_t i = 0; i < keys.size(); i++) {
93    mount.create_keys.push_back(keys[i]);
94  }
95  UserContext context(user_id);
96  Key key(keys.front().secret);
97  key.SetLabel(keys.front().label);
98  context.SetKey(key);
99
100  cryptohome::HomedirMethods::GetInstance()->MountEx(
101      id,
102      auth,
103      mount,
104      base::Bind(&ExtendedAuthenticatorImpl::OnMountComplete,
105                 this,
106                 "MountEx",
107                 context,
108                 success_callback));
109}
110
111void ExtendedAuthenticatorImpl::AddKey(const UserContext& context,
112                                   const cryptohome::KeyDefinition& key,
113                                   bool replace_existing,
114                                   const base::Closure& success_callback) {
115  TransformKeyIfNeeded(context,
116                       base::Bind(&ExtendedAuthenticatorImpl::DoAddKey,
117                                  this,
118                                  key,
119                                  replace_existing,
120                                  success_callback));
121}
122
123void ExtendedAuthenticatorImpl::UpdateKeyAuthorized(
124    const UserContext& context,
125    const cryptohome::KeyDefinition& key,
126    const std::string& signature,
127    const base::Closure& success_callback) {
128  TransformKeyIfNeeded(
129      context,
130      base::Bind(&ExtendedAuthenticatorImpl::DoUpdateKeyAuthorized,
131                 this,
132                 key,
133                 signature,
134                 success_callback));
135}
136
137void ExtendedAuthenticatorImpl::RemoveKey(const UserContext& context,
138                                      const std::string& key_to_remove,
139                                      const base::Closure& success_callback) {
140  TransformKeyIfNeeded(context,
141                       base::Bind(&ExtendedAuthenticatorImpl::DoRemoveKey,
142                                  this,
143                                  key_to_remove,
144                                  success_callback));
145}
146
147void ExtendedAuthenticatorImpl::TransformKeyIfNeeded(
148    const UserContext& user_context,
149    const ContextCallback& callback) {
150  if (user_context.GetKey()->GetKeyType() != Key::KEY_TYPE_PASSWORD_PLAIN) {
151    callback.Run(user_context);
152    return;
153  }
154
155  if (!salt_obtained_) {
156    system_salt_callbacks_.push_back(
157        base::Bind(&ExtendedAuthenticatorImpl::TransformKeyIfNeeded,
158                   this,
159                   user_context,
160                   callback));
161    return;
162  }
163
164  UserContext transformed_context = user_context;
165  transformed_context.GetKey()->Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF,
166                                          system_salt_);
167  callback.Run(transformed_context);
168}
169
170ExtendedAuthenticatorImpl::~ExtendedAuthenticatorImpl() {
171}
172
173void ExtendedAuthenticatorImpl::OnSaltObtained(const std::string& system_salt) {
174  salt_obtained_ = true;
175  system_salt_ = system_salt;
176  for (std::vector<base::Closure>::const_iterator it =
177           system_salt_callbacks_.begin();
178       it != system_salt_callbacks_.end();
179       ++it) {
180    it->Run();
181  }
182  system_salt_callbacks_.clear();
183}
184
185void ExtendedAuthenticatorImpl::DoAuthenticateToMount(
186    const ResultCallback& success_callback,
187    const UserContext& user_context) {
188  RecordStartMarker("MountEx");
189
190  std::string canonicalized = gaia::CanonicalizeEmail(user_context.GetUserID());
191  cryptohome::Identification id(canonicalized);
192  const Key* const key = user_context.GetKey();
193  cryptohome::Authorization auth(key->GetSecret(), key->GetLabel());
194  cryptohome::MountParameters mount(false);
195
196  cryptohome::HomedirMethods::GetInstance()->MountEx(
197      id,
198      auth,
199      mount,
200      base::Bind(&ExtendedAuthenticatorImpl::OnMountComplete,
201                 this,
202                 "MountEx",
203                 user_context,
204                 success_callback));
205}
206
207void ExtendedAuthenticatorImpl::DoAuthenticateToCheck(
208    const base::Closure& success_callback,
209    const UserContext& user_context) {
210  RecordStartMarker("CheckKeyEx");
211
212  std::string canonicalized = gaia::CanonicalizeEmail(user_context.GetUserID());
213  cryptohome::Identification id(canonicalized);
214  const Key* const key = user_context.GetKey();
215  cryptohome::Authorization auth(key->GetSecret(), key->GetLabel());
216
217  cryptohome::HomedirMethods::GetInstance()->CheckKeyEx(
218      id,
219      auth,
220      base::Bind(&ExtendedAuthenticatorImpl::OnOperationComplete,
221                 this,
222                 "CheckKeyEx",
223                 user_context,
224                 success_callback));
225}
226
227void ExtendedAuthenticatorImpl::DoAddKey(const cryptohome::KeyDefinition& key,
228                                     bool replace_existing,
229                                     const base::Closure& success_callback,
230                                     const UserContext& user_context) {
231  RecordStartMarker("AddKeyEx");
232
233  std::string canonicalized = gaia::CanonicalizeEmail(user_context.GetUserID());
234  cryptohome::Identification id(canonicalized);
235  const Key* const auth_key = user_context.GetKey();
236  cryptohome::Authorization auth(auth_key->GetSecret(), auth_key->GetLabel());
237
238  cryptohome::HomedirMethods::GetInstance()->AddKeyEx(
239      id,
240      auth,
241      key,
242      replace_existing,
243      base::Bind(&ExtendedAuthenticatorImpl::OnOperationComplete,
244                 this,
245                 "AddKeyEx",
246                 user_context,
247                 success_callback));
248}
249
250void ExtendedAuthenticatorImpl::DoUpdateKeyAuthorized(
251    const cryptohome::KeyDefinition& key,
252    const std::string& signature,
253    const base::Closure& success_callback,
254    const UserContext& user_context) {
255  RecordStartMarker("UpdateKeyAuthorized");
256
257  std::string canonicalized = gaia::CanonicalizeEmail(user_context.GetUserID());
258  cryptohome::Identification id(canonicalized);
259  const Key* const auth_key = user_context.GetKey();
260  cryptohome::Authorization auth(auth_key->GetSecret(), auth_key->GetLabel());
261
262  cryptohome::HomedirMethods::GetInstance()->UpdateKeyEx(
263      id,
264      auth,
265      key,
266      signature,
267      base::Bind(&ExtendedAuthenticatorImpl::OnOperationComplete,
268                 this,
269                 "UpdateKeyAuthorized",
270                 user_context,
271                 success_callback));
272}
273
274void ExtendedAuthenticatorImpl::DoRemoveKey(const std::string& key_to_remove,
275                                        const base::Closure& success_callback,
276                                        const UserContext& user_context) {
277  RecordStartMarker("RemoveKeyEx");
278
279  std::string canonicalized = gaia::CanonicalizeEmail(user_context.GetUserID());
280  cryptohome::Identification id(canonicalized);
281  const Key* const auth_key = user_context.GetKey();
282  cryptohome::Authorization auth(auth_key->GetSecret(), auth_key->GetLabel());
283
284  cryptohome::HomedirMethods::GetInstance()->RemoveKeyEx(
285      id,
286      auth,
287      key_to_remove,
288      base::Bind(&ExtendedAuthenticatorImpl::OnOperationComplete,
289                 this,
290                 "RemoveKeyEx",
291                 user_context,
292                 success_callback));
293}
294
295void ExtendedAuthenticatorImpl::OnMountComplete(
296    const std::string& time_marker,
297    const UserContext& user_context,
298    const ResultCallback& success_callback,
299    bool success,
300    cryptohome::MountError return_code,
301    const std::string& mount_hash) {
302  RecordEndMarker(time_marker);
303  UserContext copy = user_context;
304  copy.SetUserIDHash(mount_hash);
305  if (return_code == cryptohome::MOUNT_ERROR_NONE) {
306    if (!success_callback.is_null())
307      success_callback.Run(mount_hash);
308    if (old_consumer_)
309      old_consumer_->OnAuthSuccess(copy);
310    return;
311  }
312  AuthState state = FAILED_MOUNT;
313  if (return_code == cryptohome::MOUNT_ERROR_TPM_COMM_ERROR ||
314      return_code == cryptohome::MOUNT_ERROR_TPM_DEFEND_LOCK ||
315      return_code == cryptohome::MOUNT_ERROR_TPM_NEEDS_REBOOT) {
316    state = FAILED_TPM;
317  }
318  if (return_code == cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST) {
319    state = NO_MOUNT;
320  }
321  if (consumer_)
322    consumer_->OnAuthenticationFailure(state);
323  if (old_consumer_) {
324    AuthFailure failure(AuthFailure::COULD_NOT_MOUNT_CRYPTOHOME);
325    old_consumer_->OnAuthFailure(failure);
326  }
327}
328
329void ExtendedAuthenticatorImpl::OnOperationComplete(
330    const std::string& time_marker,
331    const UserContext& user_context,
332    const base::Closure& success_callback,
333    bool success,
334    cryptohome::MountError return_code) {
335  RecordEndMarker(time_marker);
336  if (return_code == cryptohome::MOUNT_ERROR_NONE) {
337    if (!success_callback.is_null())
338      success_callback.Run();
339    if (old_consumer_)
340      old_consumer_->OnAuthSuccess(user_context);
341    return;
342  }
343
344  LOG(ERROR) << "Supervised user cryptohome error, code: " << return_code;
345
346  AuthState state = FAILED_MOUNT;
347
348  if (return_code == cryptohome::MOUNT_ERROR_TPM_COMM_ERROR ||
349      return_code == cryptohome::MOUNT_ERROR_TPM_DEFEND_LOCK ||
350      return_code == cryptohome::MOUNT_ERROR_TPM_NEEDS_REBOOT) {
351    state = FAILED_TPM;
352  }
353
354  if (return_code == cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST)
355    state = NO_MOUNT;
356
357  if (consumer_)
358    consumer_->OnAuthenticationFailure(state);
359
360  if (old_consumer_) {
361    AuthFailure failure(AuthFailure::UNLOCK_FAILED);
362    old_consumer_->OnAuthFailure(failure);
363  }
364}
365
366}  // namespace chromeos
367