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::ToLowerASCII(
44 base::HexEncode(reinterpret_cast<const void*>(result
), sizeof(result
)));
47 std::string
BuildRawHMACKey() {
48 scoped_ptr
<crypto::SymmetricKey
> key(crypto::SymmetricKey::GenerateRandomKey(
49 crypto::SymmetricKey::AES
, kHMACKeySizeInBits
));
50 std::string raw_result
, result
;
51 key
->GetRawKey(&raw_result
);
52 base::Base64Encode(raw_result
, &result
);
56 base::DictionaryValue
* LoadPasswordData(base::FilePath profile_dir
) {
57 JSONFileValueDeserializer
deserializer(
58 profile_dir
.Append(kPasswordUpdateFile
));
59 std::string error_message
;
60 int error_code
= JSONFileValueDeserializer::JSON_NO_ERROR
;
61 scoped_ptr
<base::Value
> value(
62 deserializer
.Deserialize(&error_code
, &error_message
));
63 if (JSONFileValueDeserializer::JSON_NO_ERROR
!= error_code
) {
64 LOG(ERROR
) << "Could not deserialize password data, error = " << error_code
65 << " / " << error_message
;
68 base::DictionaryValue
* result
;
69 if (!value
->GetAsDictionary(&result
)) {
70 LOG(ERROR
) << "Stored password data is not a dictionary";
73 ignore_result(value
.release());
77 void OnPasswordDataLoaded(
78 const SupervisedUserAuthentication::PasswordDataCallback
& success_callback
,
79 const base::Closure
& failure_callback
,
80 base::DictionaryValue
* value
) {
82 failure_callback
.Run();
85 success_callback
.Run(value
);
91 SupervisedUserAuthentication::SupervisedUserAuthentication(
92 SupervisedUserManager
* owner
)
94 stable_schema_(SCHEMA_SALT_HASHED
) {
97 SupervisedUserAuthentication::~SupervisedUserAuthentication() {}
99 SupervisedUserAuthentication::Schema
100 SupervisedUserAuthentication::GetStableSchema() {
101 return stable_schema_
;
104 UserContext
SupervisedUserAuthentication::TransformKey(
105 const UserContext
& context
) {
106 UserContext result
= context
;
107 int user_schema
= GetPasswordSchema(context
.GetUserID());
108 if (user_schema
== SCHEMA_PLAIN
)
111 if (user_schema
== SCHEMA_SALT_HASHED
) {
112 base::DictionaryValue holder
;
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);
123 NOTREACHED() << "Unknown password schema for " << context
.GetUserID();
127 bool 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
)
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
);
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
,
150 password_data
->SetStringWithoutPathExpansion(kPasswordSignature
,
153 extra_data
->SetStringWithoutPathExpansion(kPasswordEncryptionKey
,
155 extra_data
->SetStringWithoutPathExpansion(kPasswordSignatureKey
,
156 base64_signature_key
);
163 std::string
SupervisedUserAuthentication::GenerateMasterKey() {
164 char master_key_bytes
[kMasterKeySize
];
165 crypto::RandBytes(&master_key_bytes
, sizeof(master_key_bytes
));
166 return base::ToLowerASCII(
167 base::HexEncode(reinterpret_cast<const void*>(master_key_bytes
),
168 sizeof(master_key_bytes
)));
171 void 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
);
186 SupervisedUserAuthentication::Schema
187 SupervisedUserAuthentication::GetPasswordSchema(
188 const std::string
& user_id
) {
189 base::DictionaryValue holder
;
191 owner_
->GetPasswordInformation(user_id
, &holder
);
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
);
199 return schema_version
;
202 bool 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
,
215 password_data
->GetIntegerWithoutPathExpansion(kPasswordRevision
,
217 if (updated_schema
> local_schema
)
219 DCHECK_EQ(updated_schema
, local_schema
);
220 return updated_revision
> local_revision
;
223 void 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
);
240 base::DictionaryValue holder
;
241 owner_
->GetPasswordInformation(supervised_user_id
, &holder
);
242 holder
.SetBoolean(kRequirePasswordUpdate
, true);
243 owner_
->SetPasswordInformation(supervised_user_id
, &holder
);
246 bool 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
;
255 void 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
);
263 bool 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
;
272 void SupervisedUserAuthentication::MarkKeyIncomplete(const std::string
& user_id
,
274 base::DictionaryValue holder
;
275 owner_
->GetPasswordInformation(user_id
, &holder
);
276 holder
.SetBoolean(kHasIncompleteKey
, incomplete
);
277 owner_
->SetPasswordInformation(user_id
, &holder
);
280 void 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 ->GetTaskRunnerWithShutdownBehavior(
291 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN
)
293 FROM_HERE
, base::Bind(&LoadPasswordData
, profile_path
),
294 base::Bind(&OnPasswordDataLoaded
, success_callback
, failure_callback
));
297 std::string
SupervisedUserAuthentication::BuildPasswordSignature(
298 const std::string
& password
,
300 const std::string
& base64_signature_key
) {
301 ac::chrome::managedaccounts::account::Secret secret
;
302 secret
.set_revision(revision
);
303 secret
.set_secret(password
);
305 if (!secret
.SerializeToString(&buffer
))
306 LOG(FATAL
) << "Protobuf::SerializeToString failed";
307 std::string signature_key
;
308 base::Base64Decode(base64_signature_key
, &signature_key
);
310 crypto::HMAC
hmac(crypto::HMAC::SHA256
);
311 if (!hmac
.Init(signature_key
))
312 LOG(FATAL
) << "HMAC::Init failed";
314 unsigned char out_bytes
[kSignatureLength
];
315 if (!hmac
.Sign(buffer
, out_bytes
, sizeof(out_bytes
)))
316 LOG(FATAL
) << "HMAC::Sign failed";
318 std::string
raw_result(out_bytes
, out_bytes
+ sizeof(out_bytes
));
321 base::Base64Encode(raw_result
, &result
);
325 } // namespace chromeos