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 "chrome/browser/chromeos/login/supervised/supervised_user_authentication.h"
6
7#include "base/base64.h"
8#include "base/json/json_file_value_serializer.h"
9#include "base/macros.h"
10#include "base/metrics/histogram.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/string_util.h"
13#include "base/threading/sequenced_worker_pool.h"
14#include "chrome/browser/chromeos/login/supervised/supervised_user_constants.h"
15#include "chrome/browser/chromeos/login/users/supervised_user_manager.h"
16#include "chrome/browser/chromeos/profiles/profile_helper.h"
17#include "chromeos/cryptohome/signed_secret.pb.h"
18#include "chromeos/login/auth/key.h"
19#include "components/user_manager/user.h"
20#include "components/user_manager/user_manager.h"
21#include "content/public/browser/browser_thread.h"
22#include "crypto/hmac.h"
23#include "crypto/random.h"
24#include "crypto/symmetric_key.h"
25
26namespace chromeos {
27
28namespace {
29
30// Byte size of hash salt.
31const unsigned kSaltSize = 32;
32
33// Size of key signature.
34const unsigned kHMACKeySizeInBits = 256;
35const int kSignatureLength = 32;
36
37// Size of master key (in bytes).
38const int kMasterKeySize = 32;
39
40std::string CreateSalt() {
41    char result[kSaltSize];
42    crypto::RandBytes(&result, sizeof(result));
43    return base::StringToLowerASCII(base::HexEncode(
44        reinterpret_cast<const void*>(result),
45        sizeof(result)));
46}
47
48std::string BuildRawHMACKey() {
49  scoped_ptr<crypto::SymmetricKey> key(crypto::SymmetricKey::GenerateRandomKey(
50      crypto::SymmetricKey::AES, kHMACKeySizeInBits));
51  std::string raw_result, result;
52  key->GetRawKey(&raw_result);
53  base::Base64Encode(raw_result, &result);
54  return result;
55}
56
57base::DictionaryValue* LoadPasswordData(base::FilePath profile_dir) {
58  JSONFileValueSerializer serializer(profile_dir.Append(kPasswordUpdateFile));
59  std::string error_message;
60  int error_code = JSONFileValueSerializer::JSON_NO_ERROR;
61  scoped_ptr<base::Value> value(
62      serializer.Deserialize(&error_code, &error_message));
63  if (JSONFileValueSerializer::JSON_NO_ERROR != error_code) {
64    LOG(ERROR) << "Could not deserialize password data, error = " << error_code
65               << " / " << error_message;
66    return NULL;
67  }
68  base::DictionaryValue* result;
69  if (!value->GetAsDictionary(&result)) {
70    LOG(ERROR) << "Stored password data is not a dictionary";
71    return NULL;
72  }
73  ignore_result(value.release());
74  return result;
75}
76
77void OnPasswordDataLoaded(
78    const SupervisedUserAuthentication::PasswordDataCallback& success_callback,
79    const base::Closure& failure_callback,
80    base::DictionaryValue* value) {
81  if (!value) {
82    failure_callback.Run();
83    return;
84  }
85  success_callback.Run(value);
86  delete value;
87}
88
89}  // namespace
90
91SupervisedUserAuthentication::SupervisedUserAuthentication(
92    SupervisedUserManager* owner)
93      : owner_(owner),
94        stable_schema_(SCHEMA_SALT_HASHED) {
95}
96
97SupervisedUserAuthentication::~SupervisedUserAuthentication() {}
98
99SupervisedUserAuthentication::Schema
100SupervisedUserAuthentication::GetStableSchema() {
101  return stable_schema_;
102}
103
104UserContext SupervisedUserAuthentication::TransformKey(
105    const UserContext& context) {
106  UserContext result = context;
107  int user_schema = GetPasswordSchema(context.GetUserID());
108  if (user_schema == SCHEMA_PLAIN)
109    return result;
110
111  if (user_schema == SCHEMA_SALT_HASHED) {
112    base::DictionaryValue holder;
113    std::string salt;
114    owner_->GetPasswordInformation(context.GetUserID(), &holder);
115    holder.GetStringWithoutPathExpansion(kSalt, &salt);
116    DCHECK(!salt.empty());
117    Key* const key = result.GetKey();
118    key->Transform(Key::KEY_TYPE_SALTED_PBKDF2_AES256_1234, salt);
119    key->SetLabel(kCryptohomeSupervisedUserKeyLabel);
120    result.SetIsUsingOAuth(false);
121    return result;
122  }
123  NOTREACHED() << "Unknown password schema for " << context.GetUserID();
124  return context;
125}
126
127bool SupervisedUserAuthentication::FillDataForNewUser(
128    const std::string& user_id,
129    const std::string& password,
130    base::DictionaryValue* password_data,
131    base::DictionaryValue* extra_data) {
132  Schema schema = stable_schema_;
133  if (schema == SCHEMA_PLAIN)
134    return false;
135
136  if (schema == SCHEMA_SALT_HASHED) {
137    password_data->SetIntegerWithoutPathExpansion(kSchemaVersion, schema);
138    std::string salt = CreateSalt();
139    password_data->SetStringWithoutPathExpansion(kSalt, salt);
140    int revision = kMinPasswordRevision;
141    password_data->SetIntegerWithoutPathExpansion(kPasswordRevision, revision);
142    Key key(password);
143    key.Transform(Key::KEY_TYPE_SALTED_PBKDF2_AES256_1234, salt);
144    const std::string salted_password = key.GetSecret();
145    const std::string base64_signature_key = BuildRawHMACKey();
146    const std::string base64_signature =
147        BuildPasswordSignature(salted_password, revision, base64_signature_key);
148    password_data->SetStringWithoutPathExpansion(kEncryptedPassword,
149                                                 salted_password);
150    password_data->SetStringWithoutPathExpansion(kPasswordSignature,
151                                                 base64_signature);
152
153    extra_data->SetStringWithoutPathExpansion(kPasswordEncryptionKey,
154                                              BuildRawHMACKey());
155    extra_data->SetStringWithoutPathExpansion(kPasswordSignatureKey,
156                                              base64_signature_key);
157    return true;
158  }
159  NOTREACHED();
160  return false;
161}
162
163std::string SupervisedUserAuthentication::GenerateMasterKey() {
164  char master_key_bytes[kMasterKeySize];
165  crypto::RandBytes(&master_key_bytes, sizeof(master_key_bytes));
166  return base::StringToLowerASCII(
167      base::HexEncode(reinterpret_cast<const void*>(master_key_bytes),
168                      sizeof(master_key_bytes)));
169}
170
171void SupervisedUserAuthentication::StorePasswordData(
172    const std::string& user_id,
173    const base::DictionaryValue& password_data) {
174  base::DictionaryValue holder;
175  owner_->GetPasswordInformation(user_id, &holder);
176  const base::Value* value;
177  if (password_data.GetWithoutPathExpansion(kSchemaVersion, &value))
178      holder.SetWithoutPathExpansion(kSchemaVersion, value->DeepCopy());
179  if (password_data.GetWithoutPathExpansion(kSalt, &value))
180      holder.SetWithoutPathExpansion(kSalt, value->DeepCopy());
181  if (password_data.GetWithoutPathExpansion(kPasswordRevision, &value))
182      holder.SetWithoutPathExpansion(kPasswordRevision, value->DeepCopy());
183  owner_->SetPasswordInformation(user_id, &holder);
184}
185
186SupervisedUserAuthentication::Schema
187SupervisedUserAuthentication::GetPasswordSchema(
188  const std::string& user_id) {
189  base::DictionaryValue holder;
190
191  owner_->GetPasswordInformation(user_id, &holder);
192  // Default version.
193  int schema_version_index;
194  Schema schema_version = SCHEMA_PLAIN;
195  if (holder.GetIntegerWithoutPathExpansion(kSchemaVersion,
196                                            &schema_version_index)) {
197    schema_version = static_cast<Schema>(schema_version_index);
198  }
199  return schema_version;
200}
201
202bool SupervisedUserAuthentication::NeedPasswordChange(
203    const std::string& user_id,
204    const base::DictionaryValue* password_data) {
205  base::DictionaryValue local;
206  owner_->GetPasswordInformation(user_id, &local);
207  int local_schema = SCHEMA_PLAIN;
208  int local_revision = kMinPasswordRevision;
209  int updated_schema = SCHEMA_PLAIN;
210  int updated_revision = kMinPasswordRevision;
211  local.GetIntegerWithoutPathExpansion(kSchemaVersion, &local_schema);
212  local.GetIntegerWithoutPathExpansion(kPasswordRevision, &local_revision);
213  password_data->GetIntegerWithoutPathExpansion(kSchemaVersion,
214                                                &updated_schema);
215  password_data->GetIntegerWithoutPathExpansion(kPasswordRevision,
216                                                &updated_revision);
217  if (updated_schema > local_schema)
218    return true;
219  DCHECK_EQ(updated_schema, local_schema);
220  return updated_revision > local_revision;
221}
222
223void SupervisedUserAuthentication::ScheduleSupervisedPasswordChange(
224    const std::string& supervised_user_id,
225    const base::DictionaryValue* password_data) {
226  const user_manager::User* user =
227      user_manager::UserManager::Get()->FindUser(supervised_user_id);
228  base::FilePath profile_path = ProfileHelper::GetProfilePathByUserIdHash(
229      user->username_hash());
230  JSONFileValueSerializer serializer(profile_path.Append(kPasswordUpdateFile));
231  if (!serializer.Serialize(*password_data)) {
232    LOG(ERROR) << "Failed to schedule password update for supervised user "
233               << supervised_user_id;
234    UMA_HISTOGRAM_ENUMERATION(
235        "ManagedUsers.ChromeOS.PasswordChange",
236        SupervisedUserAuthentication::PASSWORD_CHANGE_FAILED_STORE_DATA,
237        SupervisedUserAuthentication::PASSWORD_CHANGE_RESULT_MAX_VALUE);
238    return;
239  }
240  base::DictionaryValue holder;
241  owner_->GetPasswordInformation(supervised_user_id, &holder);
242  holder.SetBoolean(kRequirePasswordUpdate, true);
243  owner_->SetPasswordInformation(supervised_user_id, &holder);
244}
245
246bool SupervisedUserAuthentication::HasScheduledPasswordUpdate(
247    const std::string& user_id) {
248  base::DictionaryValue holder;
249  owner_->GetPasswordInformation(user_id, &holder);
250  bool require_update = false;
251  holder.GetBoolean(kRequirePasswordUpdate, &require_update);
252  return require_update;
253}
254
255void SupervisedUserAuthentication::ClearScheduledPasswordUpdate(
256    const std::string& user_id) {
257  base::DictionaryValue holder;
258  owner_->GetPasswordInformation(user_id, &holder);
259  holder.SetBoolean(kRequirePasswordUpdate, false);
260  owner_->SetPasswordInformation(user_id, &holder);
261}
262
263bool SupervisedUserAuthentication::HasIncompleteKey(
264    const std::string& user_id) {
265  base::DictionaryValue holder;
266  owner_->GetPasswordInformation(user_id, &holder);
267  bool incomplete_key = false;
268  holder.GetBoolean(kHasIncompleteKey, &incomplete_key);
269  return incomplete_key;
270}
271
272void SupervisedUserAuthentication::MarkKeyIncomplete(const std::string& user_id,
273                                                     bool incomplete) {
274  base::DictionaryValue holder;
275  owner_->GetPasswordInformation(user_id, &holder);
276  holder.SetBoolean(kHasIncompleteKey, incomplete);
277  owner_->SetPasswordInformation(user_id, &holder);
278}
279
280void SupervisedUserAuthentication::LoadPasswordUpdateData(
281    const std::string& user_id,
282    const PasswordDataCallback& success_callback,
283    const base::Closure& failure_callback) {
284  const user_manager::User* user =
285      user_manager::UserManager::Get()->FindUser(user_id);
286  base::FilePath profile_path =
287      ProfileHelper::GetProfilePathByUserIdHash(user->username_hash());
288  PostTaskAndReplyWithResult(
289      content::BrowserThread::GetBlockingPool(),
290      FROM_HERE,
291      base::Bind(&LoadPasswordData, profile_path),
292      base::Bind(&OnPasswordDataLoaded, success_callback, failure_callback));
293}
294
295std::string SupervisedUserAuthentication::BuildPasswordSignature(
296    const std::string& password,
297    int revision,
298    const std::string& base64_signature_key) {
299  ac::chrome::managedaccounts::account::Secret secret;
300  secret.set_revision(revision);
301  secret.set_secret(password);
302  std::string buffer;
303  if (!secret.SerializeToString(&buffer))
304    LOG(FATAL) << "Protobuf::SerializeToString failed";
305  std::string signature_key;
306  base::Base64Decode(base64_signature_key, &signature_key);
307
308  crypto::HMAC hmac(crypto::HMAC::SHA256);
309  if (!hmac.Init(signature_key))
310    LOG(FATAL) << "HMAC::Init failed";
311
312  unsigned char out_bytes[kSignatureLength];
313  if (!hmac.Sign(buffer, out_bytes, sizeof(out_bytes)))
314    LOG(FATAL) << "HMAC::Sign failed";
315
316  std::string raw_result(out_bytes, out_bytes + sizeof(out_bytes));
317
318  std::string result;
319  base::Base64Encode(raw_result, &result);
320  return result;
321}
322
323}  // namespace chromeos
324