Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / chromeos / login / supervised / supervised_user_authentication.cc
blobb4a64a35b45edd4d75d1790465b6a74a07777263
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.
5 #include "chrome/browser/chromeos/login/supervised/supervised_user_authentication.h"
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"
26 namespace chromeos {
28 namespace {
30 // Byte size of hash salt.
31 const unsigned kSaltSize = 32;
33 // Size of key signature.
34 const unsigned kHMACKeySizeInBits = 256;
35 const int kSignatureLength = 32;
37 // Size of master key (in bytes).
38 const int kMasterKeySize = 32;
40 std::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)));
48 std::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;
57 base::DictionaryValue* LoadPasswordData(base::FilePath profile_dir) {
58 JSONFileValueDeserializer deserializer(
59 profile_dir.Append(kPasswordUpdateFile));
60 std::string error_message;
61 int error_code = JSONFileValueDeserializer::JSON_NO_ERROR;
62 scoped_ptr<base::Value> value(
63 deserializer.Deserialize(&error_code, &error_message));
64 if (JSONFileValueDeserializer::JSON_NO_ERROR != error_code) {
65 LOG(ERROR) << "Could not deserialize password data, error = " << error_code
66 << " / " << error_message;
67 return NULL;
69 base::DictionaryValue* result;
70 if (!value->GetAsDictionary(&result)) {
71 LOG(ERROR) << "Stored password data is not a dictionary";
72 return NULL;
74 ignore_result(value.release());
75 return result;
78 void OnPasswordDataLoaded(
79 const SupervisedUserAuthentication::PasswordDataCallback& success_callback,
80 const base::Closure& failure_callback,
81 base::DictionaryValue* value) {
82 if (!value) {
83 failure_callback.Run();
84 return;
86 success_callback.Run(value);
87 delete value;
90 } // namespace
92 SupervisedUserAuthentication::SupervisedUserAuthentication(
93 SupervisedUserManager* owner)
94 : owner_(owner),
95 stable_schema_(SCHEMA_SALT_HASHED) {
98 SupervisedUserAuthentication::~SupervisedUserAuthentication() {}
100 SupervisedUserAuthentication::Schema
101 SupervisedUserAuthentication::GetStableSchema() {
102 return stable_schema_;
105 UserContext SupervisedUserAuthentication::TransformKey(
106 const UserContext& context) {
107 UserContext result = context;
108 int user_schema = GetPasswordSchema(context.GetUserID());
109 if (user_schema == SCHEMA_PLAIN)
110 return result;
112 if (user_schema == SCHEMA_SALT_HASHED) {
113 base::DictionaryValue holder;
114 std::string salt;
115 owner_->GetPasswordInformation(context.GetUserID(), &holder);
116 holder.GetStringWithoutPathExpansion(kSalt, &salt);
117 DCHECK(!salt.empty());
118 Key* const key = result.GetKey();
119 key->Transform(Key::KEY_TYPE_SALTED_PBKDF2_AES256_1234, salt);
120 key->SetLabel(kCryptohomeSupervisedUserKeyLabel);
121 result.SetIsUsingOAuth(false);
122 return result;
124 NOTREACHED() << "Unknown password schema for " << context.GetUserID();
125 return context;
128 bool SupervisedUserAuthentication::FillDataForNewUser(
129 const std::string& user_id,
130 const std::string& password,
131 base::DictionaryValue* password_data,
132 base::DictionaryValue* extra_data) {
133 Schema schema = stable_schema_;
134 if (schema == SCHEMA_PLAIN)
135 return false;
137 if (schema == SCHEMA_SALT_HASHED) {
138 password_data->SetIntegerWithoutPathExpansion(kSchemaVersion, schema);
139 std::string salt = CreateSalt();
140 password_data->SetStringWithoutPathExpansion(kSalt, salt);
141 int revision = kMinPasswordRevision;
142 password_data->SetIntegerWithoutPathExpansion(kPasswordRevision, revision);
143 Key key(password);
144 key.Transform(Key::KEY_TYPE_SALTED_PBKDF2_AES256_1234, salt);
145 const std::string salted_password = key.GetSecret();
146 const std::string base64_signature_key = BuildRawHMACKey();
147 const std::string base64_signature =
148 BuildPasswordSignature(salted_password, revision, base64_signature_key);
149 password_data->SetStringWithoutPathExpansion(kEncryptedPassword,
150 salted_password);
151 password_data->SetStringWithoutPathExpansion(kPasswordSignature,
152 base64_signature);
154 extra_data->SetStringWithoutPathExpansion(kPasswordEncryptionKey,
155 BuildRawHMACKey());
156 extra_data->SetStringWithoutPathExpansion(kPasswordSignatureKey,
157 base64_signature_key);
158 return true;
160 NOTREACHED();
161 return false;
164 std::string SupervisedUserAuthentication::GenerateMasterKey() {
165 char master_key_bytes[kMasterKeySize];
166 crypto::RandBytes(&master_key_bytes, sizeof(master_key_bytes));
167 return base::StringToLowerASCII(
168 base::HexEncode(reinterpret_cast<const void*>(master_key_bytes),
169 sizeof(master_key_bytes)));
172 void SupervisedUserAuthentication::StorePasswordData(
173 const std::string& user_id,
174 const base::DictionaryValue& password_data) {
175 base::DictionaryValue holder;
176 owner_->GetPasswordInformation(user_id, &holder);
177 const base::Value* value;
178 if (password_data.GetWithoutPathExpansion(kSchemaVersion, &value))
179 holder.SetWithoutPathExpansion(kSchemaVersion, value->DeepCopy());
180 if (password_data.GetWithoutPathExpansion(kSalt, &value))
181 holder.SetWithoutPathExpansion(kSalt, value->DeepCopy());
182 if (password_data.GetWithoutPathExpansion(kPasswordRevision, &value))
183 holder.SetWithoutPathExpansion(kPasswordRevision, value->DeepCopy());
184 owner_->SetPasswordInformation(user_id, &holder);
187 SupervisedUserAuthentication::Schema
188 SupervisedUserAuthentication::GetPasswordSchema(
189 const std::string& user_id) {
190 base::DictionaryValue holder;
192 owner_->GetPasswordInformation(user_id, &holder);
193 // Default version.
194 int schema_version_index;
195 Schema schema_version = SCHEMA_PLAIN;
196 if (holder.GetIntegerWithoutPathExpansion(kSchemaVersion,
197 &schema_version_index)) {
198 schema_version = static_cast<Schema>(schema_version_index);
200 return schema_version;
203 bool SupervisedUserAuthentication::NeedPasswordChange(
204 const std::string& user_id,
205 const base::DictionaryValue* password_data) {
206 base::DictionaryValue local;
207 owner_->GetPasswordInformation(user_id, &local);
208 int local_schema = SCHEMA_PLAIN;
209 int local_revision = kMinPasswordRevision;
210 int updated_schema = SCHEMA_PLAIN;
211 int updated_revision = kMinPasswordRevision;
212 local.GetIntegerWithoutPathExpansion(kSchemaVersion, &local_schema);
213 local.GetIntegerWithoutPathExpansion(kPasswordRevision, &local_revision);
214 password_data->GetIntegerWithoutPathExpansion(kSchemaVersion,
215 &updated_schema);
216 password_data->GetIntegerWithoutPathExpansion(kPasswordRevision,
217 &updated_revision);
218 if (updated_schema > local_schema)
219 return true;
220 DCHECK_EQ(updated_schema, local_schema);
221 return updated_revision > local_revision;
224 void SupervisedUserAuthentication::ScheduleSupervisedPasswordChange(
225 const std::string& supervised_user_id,
226 const base::DictionaryValue* password_data) {
227 const user_manager::User* user =
228 user_manager::UserManager::Get()->FindUser(supervised_user_id);
229 base::FilePath profile_path = ProfileHelper::GetProfilePathByUserIdHash(
230 user->username_hash());
231 JSONFileValueSerializer serializer(profile_path.Append(kPasswordUpdateFile));
232 if (!serializer.Serialize(*password_data)) {
233 LOG(ERROR) << "Failed to schedule password update for supervised user "
234 << supervised_user_id;
235 UMA_HISTOGRAM_ENUMERATION(
236 "ManagedUsers.ChromeOS.PasswordChange",
237 SupervisedUserAuthentication::PASSWORD_CHANGE_FAILED_STORE_DATA,
238 SupervisedUserAuthentication::PASSWORD_CHANGE_RESULT_MAX_VALUE);
239 return;
241 base::DictionaryValue holder;
242 owner_->GetPasswordInformation(supervised_user_id, &holder);
243 holder.SetBoolean(kRequirePasswordUpdate, true);
244 owner_->SetPasswordInformation(supervised_user_id, &holder);
247 bool SupervisedUserAuthentication::HasScheduledPasswordUpdate(
248 const std::string& user_id) {
249 base::DictionaryValue holder;
250 owner_->GetPasswordInformation(user_id, &holder);
251 bool require_update = false;
252 holder.GetBoolean(kRequirePasswordUpdate, &require_update);
253 return require_update;
256 void SupervisedUserAuthentication::ClearScheduledPasswordUpdate(
257 const std::string& user_id) {
258 base::DictionaryValue holder;
259 owner_->GetPasswordInformation(user_id, &holder);
260 holder.SetBoolean(kRequirePasswordUpdate, false);
261 owner_->SetPasswordInformation(user_id, &holder);
264 bool SupervisedUserAuthentication::HasIncompleteKey(
265 const std::string& user_id) {
266 base::DictionaryValue holder;
267 owner_->GetPasswordInformation(user_id, &holder);
268 bool incomplete_key = false;
269 holder.GetBoolean(kHasIncompleteKey, &incomplete_key);
270 return incomplete_key;
273 void SupervisedUserAuthentication::MarkKeyIncomplete(const std::string& user_id,
274 bool incomplete) {
275 base::DictionaryValue holder;
276 owner_->GetPasswordInformation(user_id, &holder);
277 holder.SetBoolean(kHasIncompleteKey, incomplete);
278 owner_->SetPasswordInformation(user_id, &holder);
281 void SupervisedUserAuthentication::LoadPasswordUpdateData(
282 const std::string& user_id,
283 const PasswordDataCallback& success_callback,
284 const base::Closure& failure_callback) {
285 const user_manager::User* user =
286 user_manager::UserManager::Get()->FindUser(user_id);
287 base::FilePath profile_path =
288 ProfileHelper::GetProfilePathByUserIdHash(user->username_hash());
289 PostTaskAndReplyWithResult(
290 content::BrowserThread::GetBlockingPool()
291 ->GetTaskRunnerWithShutdownBehavior(
292 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)
293 .get(),
294 FROM_HERE, base::Bind(&LoadPasswordData, profile_path),
295 base::Bind(&OnPasswordDataLoaded, success_callback, failure_callback));
298 std::string SupervisedUserAuthentication::BuildPasswordSignature(
299 const std::string& password,
300 int revision,
301 const std::string& base64_signature_key) {
302 ac::chrome::managedaccounts::account::Secret secret;
303 secret.set_revision(revision);
304 secret.set_secret(password);
305 std::string buffer;
306 if (!secret.SerializeToString(&buffer))
307 LOG(FATAL) << "Protobuf::SerializeToString failed";
308 std::string signature_key;
309 base::Base64Decode(base64_signature_key, &signature_key);
311 crypto::HMAC hmac(crypto::HMAC::SHA256);
312 if (!hmac.Init(signature_key))
313 LOG(FATAL) << "HMAC::Init failed";
315 unsigned char out_bytes[kSignatureLength];
316 if (!hmac.Sign(buffer, out_bytes, sizeof(out_bytes)))
317 LOG(FATAL) << "HMAC::Sign failed";
319 std::string raw_result(out_bytes, out_bytes + sizeof(out_bytes));
321 std::string result;
322 base::Base64Encode(raw_result, &result);
323 return result;
326 } // namespace chromeos