OLD | NEW |
| (Empty) |
1 // Copyright 2013 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/managed/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/managed/locally_managed_user_constants.h
" | |
15 #include "chrome/browser/chromeos/login/users/supervised_user_manager.h" | |
16 #include "chrome/browser/chromeos/login/users/user.h" | |
17 #include "chrome/browser/chromeos/login/users/user_manager.h" | |
18 #include "chrome/browser/chromeos/profiles/profile_helper.h" | |
19 #include "chromeos/cryptohome/signed_secret.pb.h" | |
20 #include "chromeos/login/auth/key.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 | |
26 namespace chromeos { | |
27 | |
28 namespace { | |
29 | |
30 // Byte size of hash salt. | |
31 const unsigned kSaltSize = 32; | |
32 | |
33 // Size of key signature. | |
34 const unsigned kHMACKeySizeInBits = 256; | |
35 const int kSignatureLength = 32; | |
36 | |
37 // Size of master key (in bytes). | |
38 const int kMasterKeySize = 32; | |
39 | |
40 std::string CreateSalt() { | |
41 char result[kSaltSize]; | |
42 crypto::RandBytes(&result, sizeof(result)); | |
43 return StringToLowerASCII(base::HexEncode( | |
44 reinterpret_cast<const void*>(result), | |
45 sizeof(result))); | |
46 } | |
47 | |
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; | |
55 } | |
56 | |
57 base::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 | |
77 void 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 | |
91 SupervisedUserAuthentication::SupervisedUserAuthentication( | |
92 SupervisedUserManager* owner) | |
93 : owner_(owner), | |
94 stable_schema_(SCHEMA_SALT_HASHED) { | |
95 } | |
96 | |
97 SupervisedUserAuthentication::~SupervisedUserAuthentication() {} | |
98 | |
99 SupervisedUserAuthentication::Schema | |
100 SupervisedUserAuthentication::GetStableSchema() { | |
101 return stable_schema_; | |
102 } | |
103 | |
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) | |
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 | |
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) | |
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 | |
163 std::string SupervisedUserAuthentication::GenerateMasterKey() { | |
164 char master_key_bytes[kMasterKeySize]; | |
165 crypto::RandBytes(&master_key_bytes, sizeof(master_key_bytes)); | |
166 return StringToLowerASCII( | |
167 base::HexEncode(reinterpret_cast<const void*>(master_key_bytes), | |
168 sizeof(master_key_bytes))); | |
169 } | |
170 | |
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); | |
184 } | |
185 | |
186 SupervisedUserAuthentication::Schema | |
187 SupervisedUserAuthentication::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 | |
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, | |
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 | |
223 void SupervisedUserAuthentication::ScheduleSupervisedPasswordChange( | |
224 const std::string& supervised_user_id, | |
225 const base::DictionaryValue* password_data) { | |
226 const User* user = UserManager::Get()->FindUser(supervised_user_id); | |
227 base::FilePath profile_path = ProfileHelper::GetProfilePathByUserIdHash( | |
228 user->username_hash()); | |
229 JSONFileValueSerializer serializer(profile_path.Append(kPasswordUpdateFile)); | |
230 if (!serializer.Serialize(*password_data)) { | |
231 LOG(ERROR) << "Failed to schedule password update for supervised user " | |
232 << supervised_user_id; | |
233 UMA_HISTOGRAM_ENUMERATION( | |
234 "ManagedUsers.ChromeOS.PasswordChange", | |
235 SupervisedUserAuthentication::PASSWORD_CHANGE_FAILED_STORE_DATA, | |
236 SupervisedUserAuthentication::PASSWORD_CHANGE_RESULT_MAX_VALUE); | |
237 return; | |
238 } | |
239 base::DictionaryValue holder; | |
240 owner_->GetPasswordInformation(supervised_user_id, &holder); | |
241 holder.SetBoolean(kRequirePasswordUpdate, true); | |
242 owner_->SetPasswordInformation(supervised_user_id, &holder); | |
243 } | |
244 | |
245 bool SupervisedUserAuthentication::HasScheduledPasswordUpdate( | |
246 const std::string& user_id) { | |
247 base::DictionaryValue holder; | |
248 owner_->GetPasswordInformation(user_id, &holder); | |
249 bool require_update = false; | |
250 holder.GetBoolean(kRequirePasswordUpdate, &require_update); | |
251 return require_update; | |
252 } | |
253 | |
254 void SupervisedUserAuthentication::ClearScheduledPasswordUpdate( | |
255 const std::string& user_id) { | |
256 base::DictionaryValue holder; | |
257 owner_->GetPasswordInformation(user_id, &holder); | |
258 holder.SetBoolean(kRequirePasswordUpdate, false); | |
259 owner_->SetPasswordInformation(user_id, &holder); | |
260 } | |
261 | |
262 bool SupervisedUserAuthentication::HasIncompleteKey( | |
263 const std::string& user_id) { | |
264 base::DictionaryValue holder; | |
265 owner_->GetPasswordInformation(user_id, &holder); | |
266 bool incomplete_key = false; | |
267 holder.GetBoolean(kHasIncompleteKey, &incomplete_key); | |
268 return incomplete_key; | |
269 } | |
270 | |
271 void SupervisedUserAuthentication::MarkKeyIncomplete(const std::string& user_id, | |
272 bool incomplete) { | |
273 base::DictionaryValue holder; | |
274 owner_->GetPasswordInformation(user_id, &holder); | |
275 holder.SetBoolean(kHasIncompleteKey, incomplete); | |
276 owner_->SetPasswordInformation(user_id, &holder); | |
277 } | |
278 | |
279 void SupervisedUserAuthentication::LoadPasswordUpdateData( | |
280 const std::string& user_id, | |
281 const PasswordDataCallback& success_callback, | |
282 const base::Closure& failure_callback) { | |
283 const User* user = UserManager::Get()->FindUser(user_id); | |
284 base::FilePath profile_path = | |
285 ProfileHelper::GetProfilePathByUserIdHash(user->username_hash()); | |
286 PostTaskAndReplyWithResult( | |
287 content::BrowserThread::GetBlockingPool(), | |
288 FROM_HERE, | |
289 base::Bind(&LoadPasswordData, profile_path), | |
290 base::Bind(&OnPasswordDataLoaded, success_callback, failure_callback)); | |
291 } | |
292 | |
293 std::string SupervisedUserAuthentication::BuildPasswordSignature( | |
294 const std::string& password, | |
295 int revision, | |
296 const std::string& base64_signature_key) { | |
297 ac::chrome::managedaccounts::account::Secret secret; | |
298 secret.set_revision(revision); | |
299 secret.set_secret(password); | |
300 std::string buffer; | |
301 if (!secret.SerializeToString(&buffer)) | |
302 LOG(FATAL) << "Protobuf::SerializeToString failed"; | |
303 std::string signature_key; | |
304 base::Base64Decode(base64_signature_key, &signature_key); | |
305 | |
306 crypto::HMAC hmac(crypto::HMAC::SHA256); | |
307 if (!hmac.Init(signature_key)) | |
308 LOG(FATAL) << "HMAC::Init failed"; | |
309 | |
310 unsigned char out_bytes[kSignatureLength]; | |
311 if (!hmac.Sign(buffer, out_bytes, sizeof(out_bytes))) | |
312 LOG(FATAL) << "HMAC::Sign failed"; | |
313 | |
314 std::string raw_result(out_bytes, out_bytes + sizeof(out_bytes)); | |
315 | |
316 std::string result; | |
317 base::Base64Encode(raw_result, &result); | |
318 return result; | |
319 } | |
320 | |
321 } // namespace chromeos | |
OLD | NEW |