[M108 Migration] Encrypt password field in AutoFill DB 19/287619/5
authorv-saha <v.saha@samsung.com>
Wed, 1 Feb 2023 11:01:47 +0000 (16:31 +0530)
committerBot Blink <blinkbot@samsung.com>
Thu, 2 Feb 2023 11:22:53 +0000 (11:22 +0000)
Currently, password field is not encrypted in autofill db.
If device is rooted, the passwords in autofill db can be
easily exposed.

This patch does the following :

[1] Enables storing of passwords in autofill DB in encrypted form.
[2] If the DB already has unencrypted passwords, then firstly
    migrate them to the DB in encrypted form, and then enable [1].

Reference: https://review.tizen.org/gerrit/279301

Change-Id: I168532dbdd2f3fc68cd5a2887dcbc9e5c86957bc
Signed-off-by: v-saha <v.saha@samsung.com>
components/password_manager/core/browser/login_database.cc
components/password_manager/core/browser/login_database.h

index e208ca7..e8e6b8a 100644 (file)
@@ -773,6 +773,137 @@ LoginDatabase::LoginDatabase(const base::FilePath& db_path,
 
 LoginDatabase::~LoginDatabase() = default;
 
+#if BUILDFLAG(IS_EFL)
+bool LoginDatabase::EncryptAndMigratePasswords() const {
+  if (!db_.DoesTableExist("logins"))
+    return true;
+
+  std::list<std::pair<int, std::string>> rowid_password_pairs;
+  const std::string get_rowid_password =
+      "SELECT rowid, password_value FROM logins;";
+  sql::Statement s{db_.GetUniqueStatement(get_rowid_password.c_str())};
+
+  if (!s.is_valid())
+    return false;
+
+  int rowid{-1};
+  std::string password;
+
+  while (s.Step()) {
+    rowid = s.ColumnInt(0);
+    s.ColumnBlobAsString(1, &password);
+    rowid_password_pairs.push_back(std::make_pair(rowid, password));
+  }
+
+  if (!s.Succeeded())
+    return false;
+
+  const std::string update_password =
+      "UPDATE logins SET password_value = ? WHERE rowid = ?;";
+  s.Assign(db_.GetUniqueStatement(update_password.c_str()));
+
+  const std::string delete_row = "DELETE FROM logins WHERE rowid = ?;";
+  sql::Statement delete_row_sql{db_.GetUniqueStatement(delete_row.c_str())};
+
+  if (!s.is_valid() || !delete_row_sql.is_valid())
+    return false;
+
+  std::string encrypted_password;
+
+  for (const auto& rowid_password_pair : rowid_password_pairs) {
+    if (EncryptedString(base::UTF8ToUTF16(rowid_password_pair.second),
+                        &encrypted_password) != ENCRYPTION_RESULT_SUCCESS) {
+      LOG(ERROR) << "Unable to encrypt password.";
+      delete_row_sql.Reset(true);
+      delete_row_sql.BindInt(0, rowid_password_pair.first);
+      if (!delete_row_sql.Run())
+        return false;
+      continue;
+    }
+
+    s.BindBlob(0, encrypted_password);
+    s.BindInt(1, rowid_password_pair.first);
+
+    if (!s.Run())
+      return false;
+    s.Reset(true);
+  }
+  s.Clear();
+  delete_row_sql.Clear();
+
+  return true;
+}
+#endif
+
+#if defined(VERIFY_PASSWORD_ENCRYPTION)
+bool PrintPasswordsBeforeEncryption(
+    sql::Database* db,
+    std::map<int, std::string>& unencrypted_passwords) {
+  if (!db->DoesTableExist("logins")) {
+    LOG(INFO) << "Please refer to TIZENWF-1219 for detailed steps to test the "
+                 "autofill db passwords encryption and migration patch.";
+    return false;
+  }
+
+  const std::string get_rowid_password =
+      "SELECT rowid, password_value FROM logins;";
+  sql::Statement s{db->GetUniqueStatement(get_rowid_password.c_str())};
+
+  if (!s.is_valid())
+    return false;
+
+  int rowid{-1};
+  std::string unencrypted_password;
+
+  LOG(INFO)
+      << "========== List of unencrypted passwords in logins table ==========";
+
+  while (s.Step()) {
+    rowid = s.ColumnInt(0);
+    s.ColumnBlobAsString(1, &unencrypted_password);
+    unencrypted_passwords.insert(std::make_pair(rowid, unencrypted_password));
+    LOG(INFO) << " rowid : " << rowid
+              << " , Unencrypted Password : " << unencrypted_password;
+  }
+  s.Clear();
+  LOG(INFO) << "================== End of list ==================";
+
+  return true;
+}
+
+bool PrintPasswordsAfterEncryption(
+    sql::Database* db,
+    const std::map<int, std::string>& unencrypted_passwords) {
+  const std::string get_rowid_password =
+      "SELECT rowid, password_value FROM logins;";
+  sql::Statement s{db->GetUniqueStatement(get_rowid_password.c_str())};
+
+  if (!s.is_valid())
+    return false;
+
+  int rowid{-1};
+  std::string encrypted_password;
+
+  LOG(INFO) << "========== List of corresponding passwords after encryption "
+               "==========";
+
+  while (s.Step()) {
+    rowid = s.ColumnInt(0);
+    s.ColumnBlobAsString(1, &encrypted_password);
+
+    auto find_itr = unencrypted_passwords.find(rowid);
+    if (find_itr != unencrypted_passwords.end())
+      LOG(INFO) << "rowid : " << rowid
+                << " , Unencrypted Password : " << find_itr->second
+                << " , Encrypted Password : " << encrypted_password;
+  }
+  s.Clear();
+  LOG(INFO) << "================== End of list ==================";
+
+  return true;
+}
+#endif
+
 bool LoginDatabase::Init() {
   TRACE_EVENT0("passwords", "LoginDatabase::Init");
   db_.set_histogram_tag("Passwords");
@@ -892,6 +1023,24 @@ bool LoginDatabase::Init() {
   if (migration_success && current_version <= 15) {
     migration_success = stats_table_.MigrateToVersion(16);
   }
+
+#if defined(VERIFY_PASSWORD_ENCRYPTION)
+  std::map<int, std::string> unencrypted_passwords;
+  auto printed_unencrypted_passwords =
+      PrintPasswordsBeforeEncryption(&db_, unencrypted_passwords);
+#endif
+
+#if BUILDFLAG(IS_EFL)
+  if (migration_success && current_version <= 18) {
+    migration_success = EncryptAndMigratePasswords();
+  }
+#endif
+
+#if defined(VERIFY_PASSWORD_ENCRYPTION)
+  if (printed_unencrypted_passwords)
+    PrintPasswordsAfterEncryption(&db_, unencrypted_passwords);
+#endif
+
   if (migration_success) {
     // |migration_success| could be true when no logins have been actually
     // migrated. We should protect against downgrading the database version in
index 0040b8c..003bc85 100644 (file)
@@ -217,6 +217,13 @@ class LoginDatabase : public PasswordStoreSync::MetadataStore {
     ENCRYPTION_RESULT_SERVICE_FAILURE,
   };
 
+#if BUILDFLAG(IS_EFL)
+  // If the password is not encrypted in the existing autofill db, then
+  // this function encrypts the passwords and stores them back into
+  // the db.
+  bool EncryptAndMigratePasswords() const;
+#endif
+
   // Encrypts plain_text, setting the value of cipher_text and returning true if
   // successful, or returning false and leaving cipher_text unchanged if
   // encryption fails (e.g., if the underlying OS encryption system is