| 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 |