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"
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
),
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
);
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
;
69 base::DictionaryValue
* result
;
70 if (!value
->GetAsDictionary(&result
)) {
71 LOG(ERROR
) << "Stored password data is not a dictionary";
74 ignore_result(value
.release());
78 void OnPasswordDataLoaded(
79 const SupervisedUserAuthentication::PasswordDataCallback
& success_callback
,
80 const base::Closure
& failure_callback
,
81 base::DictionaryValue
* value
) {
83 failure_callback
.Run();
86 success_callback
.Run(value
);
92 SupervisedUserAuthentication::SupervisedUserAuthentication(
93 SupervisedUserManager
* 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
)
112 if (user_schema
== SCHEMA_SALT_HASHED
) {
113 base::DictionaryValue holder
;
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);
124 NOTREACHED() << "Unknown password schema for " << context
.GetUserID();
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
)
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
);
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
,
151 password_data
->SetStringWithoutPathExpansion(kPasswordSignature
,
154 extra_data
->SetStringWithoutPathExpansion(kPasswordEncryptionKey
,
156 extra_data
->SetStringWithoutPathExpansion(kPasswordSignatureKey
,
157 base64_signature_key
);
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
);
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
,
216 password_data
->GetIntegerWithoutPathExpansion(kPasswordRevision
,
218 if (updated_schema
> local_schema
)
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
);
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
,
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
)
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
,
301 const std::string
& base64_signature_key
) {
302 ac::chrome::managedaccounts::account::Secret secret
;
303 secret
.set_revision(revision
);
304 secret
.set_secret(password
);
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
));
322 base::Base64Encode(raw_result
, &result
);
326 } // namespace chromeos