Home | History | Annotate | Download | only in supervised
      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.
      4 
      5 #include "chrome/browser/chromeos/login/supervised/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/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"
     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 base::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 base::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_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);
    238     return;
    239   }
    240   base::DictionaryValue holder;
    241   owner_->GetPasswordInformation(supervised_user_id, &holder);
    242   holder.SetBoolean(kRequirePasswordUpdate, true);
    243   owner_->SetPasswordInformation(supervised_user_id, &holder);
    244 }
    245 
    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;
    253 }
    254 
    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);
    261 }
    262 
    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;
    270 }
    271 
    272 void SupervisedUserAuthentication::MarkKeyIncomplete(const std::string& user_id,
    273                                                      bool incomplete) {
    274   base::DictionaryValue holder;
    275   owner_->GetPasswordInformation(user_id, &holder);
    276   holder.SetBoolean(kHasIncompleteKey, incomplete);
    277   owner_->SetPasswordInformation(user_id, &holder);
    278 }
    279 
    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       FROM_HERE,
    291       base::Bind(&LoadPasswordData, profile_path),
    292       base::Bind(&OnPasswordDataLoaded, success_callback, failure_callback));
    293 }
    294 
    295 std::string SupervisedUserAuthentication::BuildPasswordSignature(
    296     const std::string& password,
    297     int revision,
    298     const std::string& base64_signature_key) {
    299   ac::chrome::managedaccounts::account::Secret secret;
    300   secret.set_revision(revision);
    301   secret.set_secret(password);
    302   std::string buffer;
    303   if (!secret.SerializeToString(&buffer))
    304     LOG(FATAL) << "Protobuf::SerializeToString failed";
    305   std::string signature_key;
    306   base::Base64Decode(base64_signature_key, &signature_key);
    307 
    308   crypto::HMAC hmac(crypto::HMAC::SHA256);
    309   if (!hmac.Init(signature_key))
    310     LOG(FATAL) << "HMAC::Init failed";
    311 
    312   unsigned char out_bytes[kSignatureLength];
    313   if (!hmac.Sign(buffer, out_bytes, sizeof(out_bytes)))
    314     LOG(FATAL) << "HMAC::Sign failed";
    315 
    316   std::string raw_result(out_bytes, out_bytes + sizeof(out_bytes));
    317 
    318   std::string result;
    319   base::Base64Encode(raw_result, &result);
    320   return result;
    321 }
    322 
    323 }  // namespace chromeos
    324