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/auth/key.h" 15 #include "chrome/browser/chromeos/login/managed/locally_managed_user_constants.h" 16 #include "chrome/browser/chromeos/login/users/supervised_user_manager.h" 17 #include "chrome/browser/chromeos/login/users/user.h" 18 #include "chrome/browser/chromeos/login/users/user_manager.h" 19 #include "chrome/browser/chromeos/profiles/profile_helper.h" 20 #include "chromeos/cryptohome/signed_secret.pb.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 322