Add automatic data decryption to ckm_db_tool 31/238831/14
authorMateusz Cegielka <m.cegielka@samsung.com>
Fri, 17 Jul 2020 15:30:16 +0000 (17:30 +0200)
committerMateusz Cegielka <m.cegielka@samsung.com>
Fri, 28 Aug 2020 13:46:58 +0000 (15:46 +0200)
The key manager stores key data in an encrypted database. The project
also contains a ckm_db_tool CLI utility, which decrypts the database and
launches an interactive SQL shell. However, inside the decrypted
database, the data column is still encrypted with application-specific
keys. This is inconvenient during debugging, as there is no easy way to
see the data. Also, decrypting some objects' data may require
object-specific passwords.

This patch adds a --decrypt flag, which automatically decrypts contents
of any column called "data" in all SQL query results. Additionally, if
decryption of an object requires a password, it prompts the user to
enter the password and uses it to decrypt the object's data.

The implementation finds "data", "dataType" and "idx" columns in the
output, assumes they come from the "objects" table, and uses the three
values to fetch and decrypt object data with existing CKM APIs. All rows
are prefixed with a message detailing whether the decryption was
successful.

Change-Id: I01462c5d3b24a0d7a2fea92446c4e46949b1b4f4

misc/ckm_db_tool/CMakeLists.txt
misc/ckm_db_tool/ckm-logic-ext.cpp
misc/ckm_db_tool/ckm-logic-ext.h
misc/ckm_db_tool/ckm_db_tool.cpp
misc/ckm_db_tool/db-wrapper.cpp
misc/ckm_db_tool/db-wrapper.h
misc/ckm_db_tool/decrypt.cpp [new file with mode: 0644]
misc/ckm_db_tool/decrypt.h [new file with mode: 0644]
src/manager/service/ckm-logic.h

index db6a133..c4d9c15 100644 (file)
@@ -21,6 +21,7 @@ SET(CKM_DB_TOOLS_SOURCES
     ${CMAKE_CURRENT_SOURCE_DIR}/ckm-logic-ext.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/db-wrapper.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/ui.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/decrypt.cpp
     ${KEY_MANAGER_PATH}/common/ckm-error.cpp
     ${KEY_MANAGER_PATH}/crypto/platform/decider.cpp
     ${KEY_MANAGER_PATH}/crypto/sw-backend/internals.cpp
index 3b035fc..2cb55d1 100644 (file)
@@ -27,6 +27,8 @@ namespace {
 
 const char *DB_CMD_OBJECT_SELECT = "SELECT * FROM [join_name_object_tables];";
 
+const char *DB_CMD_NAME_SELECT_BY_IDX = "SELECT name, label FROM NAMES WHERE idx = ?001;";
+
 } // anonymous namespace
 
 namespace CKM {
@@ -55,6 +57,32 @@ void CKMLogicExt::saveRow(uid_t user, const DB::Row &row)
        m_userDataMap[user].database.saveRow(row);
 }
 
+NameEntry CKMLogicExt::getNameByIdx(uid_t user, int idx)
+{
+       DB::SqlConnection::DataCommandUniquePtr selectCommand =
+               m_userDataMap[user].database.m_connection->PrepareDataCommand(DB_CMD_NAME_SELECT_BY_IDX);
+       selectCommand->BindInteger(1, idx);
+
+       if (!selectCommand->Step())
+               ThrowErr(Exc::DatabaseFailed, "getting name from database failed");
+
+       NameEntry name;
+       name.name = selectCommand->GetColumnString(0);
+       name.owner = selectCommand->GetColumnString(1);
+       return name;
+}
+
+int CKMLogicExt::readDataHelperProtected(
+       const Credentials &cred,
+       DataType dataType,
+       const Name &name,
+       const ClientId &explicitOwner,
+       const Password &password,
+       Crypto::GObjUPtr &obj)
+{
+       return readDataHelper(false, cred, dataType, name, explicitOwner, password, obj);
+}
+
 } // namespace CKM
 
 
index c988179..4ee1d77 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2000 - 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2000 - 2020 Samsung Electronics Co., Ltd All Rights Reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
 #include <dpl/db/sql_connection.h>
 
 namespace CKM {
+
+struct NameEntry {
+       Name name;
+       ClientId owner;
+};
+
 struct CKMLogicExt : public CKMLogic {
        CKMLogicExt() : m_systemDbUnlocked(false) {}
 
        DB::SqlConnection::Output Execute(uid_t user, const std::string &cmd);
        DB::RowVector getRows(uid_t user);
        void saveRow(uid_t user, const DB::Row &row);
+       NameEntry getNameByIdx(uid_t user, int idx);
+       int readDataHelperProtected(
+               const Credentials &cred,
+               DataType dataType,
+               const Name &name,
+               const ClientId &explicitOwner,
+               const Password &password,
+               Crypto::GObjUPtr &obj);
 
 private:
        bool m_systemDbUnlocked;
 };
+
 } // namespace CKM
index 6c31dc7..1846b62 100644 (file)
@@ -40,6 +40,7 @@ void usage()
        cout << "  -u, --uid UID         User id as in <TZ_SYS_DATA>/ckm/db-<uid> - default value 0\n";
        cout << "  -p, --pass PASSWORD   Password used for database encryption. For system database (uid < 5000) no password should be used.\n";
        cout << "  -c, --cmd SQLCOMMAND  Sqlite3 command to execute on database. If command not provided tool will enter interactive mode.\n";
+       cout << "      --decrypt         Enables automatic decryption of the data column.\n";
        cout << "  -h, --help            Shows this help.\n";
        cout << "Example: Open database for user 5000 and select all data from table names\n";
        cout << "  cmd_db_tool -u 5000 -p P45W0RD \"select * from names\"\n";
@@ -65,15 +66,17 @@ int main(int argc, char *argv[])
                uid_t uid = 0;
                Password pass;
                std::string argcmd;
+               bool shouldDecrypt = false;
                while(1) {
                        int option_index = 0;
 
                        static struct option long_options[] = {
-                               {"uid",  required_argument, 0, 'u'},
-                               {"cmd",  required_argument, 0, 'c'},
-                               {"pass", required_argument, 0, 'p'},
-                               {"help", no_argument,       0, 'h'},
-                               {0,      0,                 0, 0  }
+                               {"uid",     required_argument, 0, 'u'},
+                               {"cmd",     required_argument, 0, 'c'},
+                               {"pass",    required_argument, 0, 'p'},
+                               {"decrypt", no_argument,       0, 'd'},
+                               {"help",    no_argument,       0, 'h'},
+                               {0,         0,                 0, 0  }
                        };
 
                        int c = getopt_long(argc, argv, "u:c:p:h", long_options, &option_index);
@@ -97,6 +100,9 @@ int main(int argc, char *argv[])
                                case 'p':
                                        pass = optarg;
                                        break;
+                               case 'd':
+                                       shouldDecrypt = true;
+                                       break;
                        }
                }
 
@@ -133,7 +139,7 @@ int main(int argc, char *argv[])
                                continue;
                        }
 
-                       dbw.process(cmd);
+                       dbw.process(cmd, shouldDecrypt);
 
                        if (!argcmd.empty())
                                break;
index 0d9ba91..4f9498b 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <string>
 #include <algorithm>
+#include <utility>
 
 #include <base64.h>
 
@@ -74,7 +75,7 @@ void DbWrapper::lock()
        m_logic.lockUserKey(m_uid);
 }
 
-void DbWrapper::process(const std::string &acmd)
+void DbWrapper::process(const std::string &acmd, bool shouldDecrypt)
 {
        try {
                std::string cmd = acmd;
@@ -93,11 +94,16 @@ void DbWrapper::process(const std::string &acmd)
                if (output.GetNames().empty())
                        return;
 
-               displayRow(output.GetNames(), trim);
+               Decrypt decryptState(output.GetNames(), m_logic, m_uid);
+               Decrypt *decrypt = nullptr;
+               if (shouldDecrypt && decryptState.canDecryptQuery())
+                       decrypt = &decryptState;
+
+               displayRow(output.GetNames(), trim, nullptr);
                std::cout << "--------------------------" << std::endl;
 
                for (const auto &row : output.GetValues()) {
-                       displayRow(row, trim);
+                       displayRow(row, trim, decrypt);
                }
        } catch (const DB::SqlConnection::Exception::Base &e) {
                UI::error() << "sql exception: " << e.GetMessage() << std::endl;
@@ -110,11 +116,18 @@ void DbWrapper::process(const std::string &acmd)
        }
 }
 
-void DbWrapper::displayRow(const DB::SqlConnection::Output::Row &row, bool trim)
+void DbWrapper::displayRow(const DB::SqlConnection::Output::Row &row, bool trim, Decrypt *decrypt)
 {
+       std::pair<std::string, bool> decrypted;
+       if (decrypt)
+               decrypted = decrypt->tryDecrypt(row);
+
        for (auto it = row.begin(); it != row.end(); it++) {
                std::string col = *it;
 
+               if (decrypted.second && it - row.begin() == decrypt->getDataColumnIndex())
+                       col = decrypted.first;
+
                /*
                 * Convert unprintable data to base64. Note that a row may contain an incomplete data
                 * since it holds a binary data as a string.
index 70aa3cf..bfc5e98 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2000 - 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *  Copyright (c) 2000 - 2020 Samsung Electronics Co., Ltd All Rights Reserved
  *
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
 #include <dpl/db/sql_connection.h>
 #include <exception.h>
 #include "ckm-logic-ext.h"
+#include "decrypt.h"
 
 namespace CKM {
 
@@ -38,12 +39,12 @@ public:
 
        int unlock();
        void lock();
-       void process(const std::string &cmd);
+       void process(const std::string &cmd, bool shouldDecrypt);
        DB::RowVector getRows();
        void saveRow(const DB::Row &row);
 
 private:
-       void displayRow(const DB::SqlConnection::Output::Row &row, bool trim);
+       void displayRow(const DB::SqlConnection::Output::Row &row, bool trim, Decrypt *decrypt);
 
        uid_t m_uid;
        Password m_pw;
diff --git a/misc/ckm_db_tool/decrypt.cpp b/misc/ckm_db_tool/decrypt.cpp
new file mode 100644 (file)
index 0000000..6187b25
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ *  Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/*
+ * @file       decrypt.cpp
+ * @author     Mateusz Cegielka (m.cegielka@samsung.com)
+ * @version    1.0
+ */
+
+#include "decrypt.h"
+#include "ui.h"
+
+#include <algorithm>
+#include <iostream>
+#include <stdexcept>
+
+#include <credentials.h>
+#include <generic-backend/gobj.h>
+
+#include <unistd.h>
+
+namespace {
+
+int findColumnByName(const std::string &name, const CKM::Decrypt::Row &columns)
+{
+       auto column = std::find(columns.begin(), columns.end(), name);
+       if (column == columns.end())
+               return -1;
+
+       return column - columns.begin();
+}
+
+std::string fmtObject(const CKM::NameEntry &name)
+{
+       return "'" + name.name + "'";
+}
+
+CKM::Password askObjectPassword(const std::string &displayName)
+{
+       std::string password = CKM::UI::promptPassword(
+               "enter password for object " + displayName + ":");
+       return CKM::Password(password.begin(), password.end());
+}
+
+} // anonymous namespace
+
+namespace CKM {
+
+Decrypt::Decrypt(const Row &columns, CKMLogicExt &logic, uid_t uid)
+       : m_logic(logic),
+         m_uid(uid),
+         m_columnData(-1),
+         m_columnDataType(-1),
+         m_columnIdx(-1)
+{
+       m_columnData = findColumnByName("data", columns);
+       if (m_columnData == -1)
+               return;
+       m_columnDataType = findColumnByName("dataType", columns);
+       m_columnIdx = findColumnByName("idx", columns);
+       if (m_columnDataType == -1 || m_columnIdx == -1)
+               UI::warning() << "unable to decrypt, missing dataType or idx columns" << std::endl;
+}
+
+bool Decrypt::canDecryptQuery() const
+{
+       return m_columnData != -1 && m_columnDataType != -1 && m_columnIdx != -1;
+}
+
+std::pair<std::string, bool> Decrypt::tryDecrypt(const Row &row) const
+{
+       NameEntry nameEntry = getNameEntry(row);
+       std::string name = fmtObject(nameEntry);
+
+       try {
+               return {decrypt(row, "", nameEntry), true};
+       } catch (const Exc::AuthenticationFailed &) {
+       }
+
+       while (true) {
+               Password password = askObjectPassword(name);
+               if (password.empty()) {
+                       UI::warning() << "skipped decrypting object " << name << std::endl;
+                       return {"", false};
+               }
+
+               try {
+                       return {decrypt(row, password, nameEntry), true};
+               } catch (const Exc::AuthenticationFailed &) {
+                       UI::error() << "authentication failed for object " << name << std::endl;
+               }
+       }
+}
+
+int Decrypt::getDataColumnIndex() const
+{
+       return m_columnData;
+}
+
+NameEntry Decrypt::getNameEntry(const Row &row) const
+{
+       int idx = std::stoi(row[m_columnIdx]);
+
+       return m_logic.getNameByIdx(m_uid, idx);
+}
+
+std::string Decrypt::decrypt(const Row &row, const Password &password,
+       const NameEntry& nameEntry) const
+{
+       DataType dataType = static_cast<DataType::Type>(std::stoi(row[m_columnDataType]));
+       Credentials credentials(m_uid, nameEntry.owner);
+       Crypto::GObjUPtr obj;
+
+       int ret = m_logic.readDataHelperProtected(credentials, dataType, nameEntry.name,
+               nameEntry.owner, password, obj);
+       if (ret != CKM_API_SUCCESS)
+               throw std::logic_error("reading row data failed");
+
+       RawBuffer decrypted = obj->getBinary();
+       UI::info() << "decrypted object " << fmtObject(nameEntry) << std::endl;
+       return std::string(decrypted.begin(), decrypted.end());
+}
+
+} // namespace CKM
diff --git a/misc/ckm_db_tool/decrypt.h b/misc/ckm_db_tool/decrypt.h
new file mode 100644 (file)
index 0000000..644b694
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ *  Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/*
+ * @file       decrypt.h
+ * @author     Mateusz Cegielka (m.cegielka@samsung.com)
+ * @version    1.0
+ */
+
+#pragma once
+
+#include <string>
+#include <utility>
+
+#include "ckm-logic-ext.h"
+#include <dpl/db/sql_connection.h>
+
+namespace CKM {
+
+class Decrypt {
+public:
+       using Row = DB::SqlConnection::Output::Row;
+
+       Decrypt(const Row &columns, CKMLogicExt &logic, uid_t uid);
+
+       bool canDecryptQuery() const;
+       std::pair<std::string, bool> tryDecrypt(const Row &row) const;
+       int getDataColumnIndex() const;
+
+private:
+       NameEntry getNameEntry(const Row &row) const;
+       std::string decrypt(const Row &row, const Password &password, const NameEntry& name) const;
+
+       CKMLogicExt &m_logic;
+       uid_t m_uid;
+       int m_columnData;
+       int m_columnDataType;
+       int m_columnIdx;
+};
+
+} // namespace CKM
index b1c87cb..c832edc 100644 (file)
@@ -321,6 +321,7 @@ private:
                DB::Row row,
                const Password &password);
 
+protected:
        int readDataHelper(
                bool exportFlag,
                const Credentials &cred,
@@ -330,6 +331,7 @@ private:
                const Password &password,
                Crypto::GObjUPtr &obj);
 
+private:
        int readDataHelper(
                bool exportFlag,
                const Credentials &cred,