Add access control code to the CKM. 49/28849/1
authorMaciej J. Karpiuk <m.karpiuk2@samsung.com>
Tue, 30 Sep 2014 12:42:56 +0000 (14:42 +0200)
committerBartlomiej Grzelewski <b.grzelewski@samsung.com>
Thu, 16 Oct 2014 13:44:30 +0000 (15:44 +0200)
Label that did not add the alias to the DB is not allowed to perform
operations on the alias. Work in progress.

Change-Id: I733448435e87251d856e9fdfd9194e798e8e3a99

src/manager/client/client-manager-impl.cpp
src/manager/common/protocols.cpp
src/manager/common/protocols.h
src/manager/service/ckm-logic.cpp
src/manager/service/ckm-logic.h
src/manager/service/ckm-service.cpp
src/manager/service/db-crypto.cpp
src/manager/service/db-crypto.h
tests/test_db_crypto.cpp

index 25eb8af..e89fd57 100644 (file)
@@ -657,16 +657,75 @@ int ManagerImpl::ocspCheck(const CertificateShPtrVector &certChain, int &ocspSta
     });
 }
 
-int ManagerImpl::allowAccess(const std::string &/*alias*/,
-                             const std::string &/*accessor*/,
-                             AccessRight /*granted*/)
+int ManagerImpl::allowAccess(const std::string &alias,
+                             const std::string &accessor,
+                             AccessRight granted)
 {
-    return CKM_API_ERROR_UNKNOWN;
+    m_counter++;
+    int my_counter = m_counter;
+    return try_catch([&] {
+        MessageBuffer send, recv;
+        Serialization::Serialize(send, static_cast<int>(LogicCommand::ALLOW_ACCESS));
+        Serialization::Serialize(send, my_counter);
+        Serialization::Serialize(send, alias);
+        Serialization::Serialize(send, accessor);
+        Serialization::Serialize(send, static_cast<int>(granted));
+
+        int retCode = sendToServer(
+            SERVICE_SOCKET_CKM_STORAGE,
+            send.Pop(),
+            recv);
+
+        if (CKM_API_SUCCESS != retCode) {
+            return retCode;
+        }
+
+        int command;
+        int counter;
+        Deserialization::Deserialize(recv, command);
+        Deserialization::Deserialize(recv, counter);
+        Deserialization::Deserialize(recv, retCode);
+
+        if (my_counter != counter) {
+            return CKM_API_ERROR_UNKNOWN;
+        }
+
+        return retCode;
+    });
 }
 
-int ManagerImpl::denyAccess(const std::string &/*alias*/, const std::string &/*accessor*/)
+int ManagerImpl::denyAccess(const std::string &alias, const std::string &accessor)
 {
-    return CKM_API_ERROR_UNKNOWN;
+    m_counter++;
+    int my_counter = m_counter;
+    return try_catch([&] {
+        MessageBuffer send, recv;
+        Serialization::Serialize(send, static_cast<int>(LogicCommand::DENY_ACCESS));
+        Serialization::Serialize(send, my_counter);
+        Serialization::Serialize(send, alias);
+        Serialization::Serialize(send, accessor);
+
+        int retCode = sendToServer(
+            SERVICE_SOCKET_CKM_STORAGE,
+            send.Pop(),
+            recv);
+
+        if (CKM_API_SUCCESS != retCode) {
+            return retCode;
+        }
+
+        int command;
+        int counter;
+        Deserialization::Deserialize(recv, command);
+        Deserialization::Deserialize(recv, counter);
+        Deserialization::Deserialize(recv, retCode);
+
+        if (my_counter != counter) {
+            return CKM_API_ERROR_UNKNOWN;
+        }
+
+        return retCode;
+    });
 }
 
 ManagerShPtr Manager::create() {
@@ -681,4 +740,3 @@ ManagerShPtr Manager::create() {
 }
 
 } // namespace CKM
-
index c3c5492..fbd050c 100644 (file)
@@ -63,6 +63,16 @@ KeyType toKeyType(DBDataType dbtype) {
     }
 }
 
+const char* toDBAccessRight(AccessRight access_right_type) {
+    switch(access_right_type) {
+    case AccessRight::AR_READ:          return "R";
+    case AccessRight::AR_READ_REMOVE:   return "RD";
+    default:
+        // TODO
+        throw 1;
+    }
+}
+
 PolicySerializable::PolicySerializable()
 {}
 
index 2755322..0714713 100644 (file)
@@ -54,7 +54,9 @@ enum class LogicCommand : int {
     GET_CHAIN_ALIAS,
     CREATE_SIGNATURE,
     VERIFY_SIGNATURE,
-    CREATE_KEY_PAIR_DSA
+    CREATE_KEY_PAIR_DSA,
+    ALLOW_ACCESS,
+    DENY_ACCESS,
     // for backward compatibility append new on the end
 };
 
@@ -76,6 +78,7 @@ enum class DBDataType : int {
 
 DBDataType toDBDataType(KeyType key);
 KeyType toKeyType(DBDataType dbDataType);
+const char* toDBAccessRight(AccessRight access_right_type);
 
 class IStream;
 
index 17053bb..29a7062 100644 (file)
@@ -333,6 +333,9 @@ RawBuffer CKMLogic::removeData(
                 LogError("No row for given alias and label");
                 retCode = CKM_API_ERROR_DB_ALIAS_UNKNOWN;
             }
+        } Catch (DBCrypto::Exception::PermissionDenied) {
+            LogError("Error: not enough permissions!");
+            retCode = CKM_API_ERROR_ACCESS_DENIED;
         } Catch (CKM::Exception) {
             LogError("Error in deleting row!");
             retCode = CKM_API_ERROR_DB_ERROR;
@@ -357,7 +360,6 @@ int CKMLogic::getDataHelper(
     const Password &password,
     DBRow &row)
 {
-
     if (0 == m_userDataMap.count(cred.uid))
         return CKM_API_ERROR_DB_LOCKED;
 
@@ -415,6 +417,9 @@ RawBuffer CKMLogic::getData(
     } catch (const CryptoLogic::Exception::Base &e) {
         LogError("CryptoLogic failed with message: " << e.GetMessage());
         retCode = CKM_API_ERROR_SERVER_ERROR;
+    } catch (const DBCrypto::Exception::PermissionDenied &e) {
+        LogError("DBCrypto failed with message: " << e.GetMessage());
+        retCode = CKM_API_ERROR_ACCESS_DENIED;
     } catch (const DBCrypto::Exception::Base &e) {
         LogError("DBCrypto failed with message: " << e.GetMessage());
         retCode = CKM_API_ERROR_DB_ERROR;
@@ -457,9 +462,9 @@ RawBuffer CKMLogic::getDataList(
         auto &handler = m_userDataMap[cred.uid];
         Try {
             if (dataType == DBDataType::CERTIFICATE || dataType == DBDataType::BINARY_DATA) {
-                handler.database.getAliases(dataType, cred.smackLabel, aliasVector);
+                handler.database.getAliases(dataType, aliasVector);
             } else {
-                handler.database.getKeyAliases(cred.smackLabel, aliasVector);
+                handler.database.getKeyAliases(aliasVector);
             }
         } Catch (CKM::Exception) {
             LogError("Failed to get aliases");
@@ -677,6 +682,9 @@ RawBuffer CKMLogic::getCertificateChain(
     } catch (const CryptoLogic::Exception::Base &e) {
         LogError("DBCyptorModule failed with message: " << e.GetMessage());
         retCode = CKM_API_ERROR_SERVER_ERROR;
+    } catch (const DBCrypto::Exception::PermissionDenied &e) {
+        LogError("DBCrypto failed with message: " << e.GetMessage());
+        retCode = CKM_API_ERROR_ACCESS_DENIED;
     } catch (const DBCrypto::Exception::Base &e) {
         LogError("DBCrypto failed with message: " << e.GetMessage());
         retCode = CKM_API_ERROR_DB_ERROR;
@@ -728,6 +736,9 @@ RawBuffer CKMLogic::createSignature(
     } catch (const CryptoLogic::Exception::Base &e) {
         LogError("CryptoLogic failed with message: " << e.GetMessage());
         retCode = CKM_API_ERROR_SERVER_ERROR;
+    } catch (const DBCrypto::Exception::PermissionDenied &e) {
+        LogError("DBCrypto failed with message: " << e.GetMessage());
+        retCode = CKM_API_ERROR_ACCESS_DENIED;
     } catch (const DBCrypto::Exception::Base &e) {
         LogError("DBCrypto failed with message: " << e.GetMessage());
         retCode = CKM_API_ERROR_DB_ERROR;
@@ -795,6 +806,9 @@ RawBuffer CKMLogic::verifySignature(
     } catch (const CryptoLogic::Exception::Base &e) {
         LogError("CryptoLogic failed with message: " << e.GetMessage());
         retCode = CKM_API_ERROR_SERVER_ERROR;
+    } catch (const DBCrypto::Exception::PermissionDenied &e) {
+        LogError("DBCrypto failed with message: " << e.GetMessage());
+        retCode = CKM_API_ERROR_ACCESS_DENIED;
     } catch (const DBCrypto::Exception::Base &e) {
         LogError("DBCrypto failed with message: " << e.GetMessage());
         retCode = CKM_API_ERROR_DB_ERROR;
@@ -810,5 +824,75 @@ RawBuffer CKMLogic::verifySignature(
 
     return response.Pop();
 }
+
+RawBuffer CKMLogic::allowAccess(
+        Credentials &cred,
+        int commandId,
+        const Alias &item_alias,
+        const std::string &accessor_label,
+        const AccessRight req_rights)
+{
+    int retCode = CKM_API_ERROR_VERIFICATION_FAILED;
+
+    if (0 < m_userDataMap.count(cred.uid))
+    {
+        Try {
+            retCode = m_userDataMap[cred.uid].database.setAccessRights(cred.smackLabel, item_alias, accessor_label, req_rights);
+        } Catch (DBCrypto::Exception::InvalidArgs) {
+            LogError("Error: invalid args!");
+            retCode = CKM_API_ERROR_INPUT_PARAM;
+        } Catch (DBCrypto::Exception::PermissionDenied) {
+            LogError("Error: not enough permissions!");
+            retCode = CKM_API_ERROR_ACCESS_DENIED;
+        } Catch (CKM::Exception) {
+            LogError("Error in set row!");
+            retCode = CKM_API_ERROR_DB_ERROR;
+        }
+    } else {
+        retCode = CKM_API_ERROR_DB_LOCKED;
+    }
+
+    MessageBuffer response;
+    Serialization::Serialize(response, static_cast<int>(LogicCommand::ALLOW_ACCESS));
+    Serialization::Serialize(response, commandId);
+    Serialization::Serialize(response, retCode);
+
+    return response.Pop();
+}
+
+RawBuffer CKMLogic::denyAccess(
+        Credentials &cred,
+        int commandId,
+        const Alias &item_alias,
+        const std::string &accessor_label)
+{
+    int retCode = CKM_API_ERROR_VERIFICATION_FAILED;
+
+    if (0 < m_userDataMap.count(cred.uid))
+    {
+        Try {
+            retCode = m_userDataMap[cred.uid].database.clearAccessRights(cred.smackLabel, item_alias, accessor_label);
+        } Catch (DBCrypto::Exception::PermissionDenied) {
+            LogError("Error: not enough permissions!");
+            retCode = CKM_API_ERROR_ACCESS_DENIED;
+        } Catch (DBCrypto::Exception::InvalidArgs) {
+            LogError("Error: permission not found!");
+            retCode = CKM_API_ERROR_INPUT_PARAM;
+        } Catch (CKM::Exception) {
+            LogError("Error in deleting row!");
+            retCode = CKM_API_ERROR_DB_ERROR;
+        }
+    } else {
+        retCode = CKM_API_ERROR_DB_LOCKED;
+    }
+
+    MessageBuffer response;
+    Serialization::Serialize(response, static_cast<int>(LogicCommand::DENY_ACCESS));
+    Serialization::Serialize(response, commandId);
+    Serialization::Serialize(response, retCode);
+
+    return response.Pop();
+}
+
 } // namespace CKM
 
index b57243c..017d0f0 100644 (file)
@@ -136,6 +136,19 @@ public:
 
     RawBuffer setCCModeStatus(CCModeState mode_status);
 
+    RawBuffer allowAccess(
+        Credentials &cred,
+        int commandId,
+        const Alias &item_alias,
+        const std::string &accessor_label,
+        const AccessRight req_rights);
+
+    RawBuffer denyAccess(
+        Credentials &cred,
+        int commandId,
+        const Alias &item_alias,
+        const std::string &accessor_label);
+
 private:
 
     int saveDataHelper(
index b64c344..f9eb34d 100644 (file)
@@ -312,6 +312,33 @@ RawBuffer CKMService::processStorage(Credentials &cred, MessageBuffer &buffer){
                 static_cast<const HashAlgorithm>(hash),
                 static_cast<const RSAPaddingAlgorithm>(padding));
         }
+        case LogicCommand::ALLOW_ACCESS:
+        {
+            Alias item_alias;
+            std::string accessor_label;
+            int req_rights;
+            Deserialization::Deserialize(buffer, item_alias);
+            Deserialization::Deserialize(buffer, accessor_label);
+            Deserialization::Deserialize(buffer, req_rights);
+            return m_logic->allowAccess(
+                cred,
+                commandId,
+                item_alias,
+                accessor_label,
+                static_cast<AccessRight>(req_rights));
+        }
+        case LogicCommand::DENY_ACCESS:
+        {
+            Alias item_alias;
+            std::string accessor_label;
+            Deserialization::Deserialize(buffer, item_alias);
+            Deserialization::Deserialize(buffer, accessor_label);
+            return m_logic->denyAccess(
+                cred,
+                commandId,
+                item_alias,
+                accessor_label);
+        }
         default:
             Throw(Exception::BrokenProtocol);
     }
index c0998e9..02a6afd 100644 (file)
@@ -31,6 +31,7 @@
 namespace {
     const char *main_table = "CKM_TABLE";
     const char *key_table = "KEY_TABLE";
+    const char *permission_table = "PERMISSION_TABLE";
 
 // CKM_TABLE (alias TEXT, label TEXT, restricted INT, exportable INT, dataType INT,
 //            algorithmType INT, encryptionScheme INT, iv BLOB, dataSize INT, data BLOB)
@@ -47,8 +48,7 @@ namespace {
             "   dataSize INTEGER NOT NULL,"
             "   data BLOB NOT NULL,"
             "   tag BLOB NOT NULL,"
-            "   PRIMARY KEY(alias, label),"
-            "   UNIQUE(alias)"
+            "   PRIMARY KEY(alias)"
             ");";
 
 
@@ -64,45 +64,37 @@ namespace {
             "   ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
 
     const char *select_alias_cmd =
-            //                                   1              2        3
-            "SELECT * FROM CKM_TABLE WHERE alias=? AND dataType=? AND label=?; ";
+            //                                   1              2
+            "SELECT * FROM CKM_TABLE WHERE alias=? AND dataType=?; ";
 
     const char *select_check_alias_cmd =
-            //                                          1           2
-            "SELECT dataType FROM CKM_TABLE WHERE alias=? AND label=?; ";
+            //                                          1
+            "SELECT dataType FROM CKM_TABLE WHERE alias=?;";
 
     const char *select_check_global_alias_cmd =
             //                                       1
-            "SELECT label FROM CKM_TABLE WHERE alias=? ;";
-
-    const char *select_count_rows_cmd =
-            //                                   1           2
-            "SELECT COUNT(alias) FROM CKM_TABLE WHERE alias=? AND label=?;";
+            "SELECT label FROM CKM_TABLE WHERE alias=?;";
 
     const char *select_key_alias_cmd =
             //                                   1
             "SELECT * FROM CKM_TABLE WHERE alias=?"
             //                     2     3
-            " AND dataType BETWEEN ? AND ? "
-            //          4
-            " AND label=? ;";
+            " AND dataType BETWEEN ? AND ?;";
 
     const char *select_key_type_cmd =
             "SELECT alias FROM CKM_TABLE WHERE "
             //                1
                 " dataType >= ? AND "
             //                2
-                " dataType <= ? AND "
-            //        3
-                " label=?;";
+                " dataType <= ?;";
 
     const char *select_type_cmd =
-            //                                     1            2
-            "SELECT alias FROM CKM_TABLE WHERE dataType=? AND label=?;";
+            //                                          1
+            "SELECT alias FROM CKM_TABLE WHERE dataType=?;";
 
     const char *delete_alias_cmd =
-            //                                 1           2
-            "DELETE FROM CKM_TABLE WHERE alias=? AND label=?;";
+            //                                 1
+            "DELETE FROM CKM_TABLE WHERE alias=?;";
 
     const char *delete_data_with_key_cmd =
             //                                 1
@@ -122,6 +114,29 @@ namespace {
             "SELECT key FROM KEY_TABLE WHERE label=?;";
     const char *delete_key_cmd =
             "DELETE FROM KEY_TABLE WHERE label=?";
+
+
+// PERMISSION_TABLE (label TEXT, label TEXT, access_flags TEXT)
+
+    const char *db_create_permission_cmd =
+            "CREATE TABLE PERMISSION_TABLE("
+            "   alias TEXT NOT NULL,"
+            "   label TEXT NOT NULL,"
+            "   accessFlags TEXT NOT NULL,"
+            "   FOREIGN KEY(alias) REFERENCES CKM_TABLE(alias) ON DELETE CASCADE,"
+            "   PRIMARY KEY(label)"
+            ");";
+
+    const char *set_permission_alias_cmd =
+            "REPLACE INTO PERMISSION_TABLE(alias, label, accessFlags) VALUES (?, ?, ?);";
+
+    const char *select_permission_cmd =
+            //                                                    1           2
+            "SELECT accessFlags FROM PERMISSION_TABLE WHERE alias=? AND label=?;";
+
+    const char *delete_permission_cmd =
+            //                                        1           2
+            "DELETE FROM PERMISSION_TABLE WHERE alias=? AND label=?;";
 }
 
 namespace CKM {
@@ -198,28 +213,34 @@ using namespace DB;
         if(!m_connection->CheckTableExist(key_table)) {
             createTable(db_create_key_cmd, key_table);
         }
+        if(!m_connection->CheckTableExist(permission_table)) {
+            createTable(db_create_permission_cmd, permission_table);
+        }
         transaction.commit();
     }
 
-    bool DBCrypto::checkGlobalAliasExist(const std::string& alias) {
+    std::string DBCrypto::getLabelForAlias(const std::string& alias) const {
         SqlConnection::DataCommandUniquePtr checkCmd =
                 m_connection->PrepareDataCommand(select_check_global_alias_cmd);
         checkCmd->BindString(1, alias.c_str());
         if(checkCmd->Step()) {
-            LogDebug("Global alias '" << alias  << "' exists already for label "
-                    << checkCmd->GetColumnString(0));
+            return checkCmd->GetColumnString(0);
+        } else
+            return std::string();
+    }
+    bool DBCrypto::checkGlobalAliasExist(const std::string& alias) const {
+        std::string label = this->getLabelForAlias(alias);
+        if(label.empty() == false) {
+            LogDebug("Global alias '" << alias  << "' exists already for label " << label);
             return true;
         } else
             return false;
     }
 
-    bool DBCrypto::checkAliasExist(
-            const std::string& alias,
-            const std::string& label) {
+    bool DBCrypto::checkAliasExist(const std::string& alias) const {
         SqlConnection::DataCommandUniquePtr checkCmd =
                 m_connection->PrepareDataCommand(select_check_alias_cmd);
         checkCmd->BindString(1, alias.c_str());
-        checkCmd->BindString(2, label.c_str());
         if(checkCmd->Step()) {
             LogDebug("Private alias '" << alias  << "' exists already for type "
                     << checkCmd->GetColumnInteger(0));
@@ -234,10 +255,9 @@ using namespace DB;
             //Sqlite does not support partial index in our version,
             //so we do it by hand
             Transaction transaction(this);
-            if(checkAliasExist(row.alias, row.smackLabel)) {
+            if(checkAliasExist(row.alias)) {
                 ThrowMsg(DBCrypto::Exception::AliasExists,
-                        "Alias exists for alias: " << row.alias
-                        << ", label: " << row.smackLabel);
+                        "Alias exists for alias: " << row.alias);
             }
 
             SqlConnection::DataCommandUniquePtr insertCommand =
@@ -281,9 +301,60 @@ using namespace DB;
         return row;
     }
 
+    std::string DBCrypto::getPermissionsForAliasAndLabel(const Alias &alias, const std::string &label) const
+    {
+        Try{
+            SqlConnection::DataCommandUniquePtr selectCommand =
+                            m_connection->PrepareDataCommand(select_permission_cmd);
+            selectCommand->BindString(1, alias.c_str());
+            selectCommand->BindString(2, label.c_str());
+
+            if(selectCommand->Step())
+                return selectCommand->GetColumnString(0);
+
+            return std::string();
+        } Catch (SqlConnection::Exception::InvalidColumn) {
+            LogError("Select statement invalid column error");
+        } Catch (SqlConnection::Exception::SyntaxError) {
+            LogError("Couldn't prepare select statement");
+        } Catch (SqlConnection::Exception::InternalError) {
+            LogError("Couldn't execute select statement");
+        }
+        return std::string();
+    }
+
+
+    bool    DBCrypto::rowAccessControlCheck(const Alias &alias,
+                                            const std::string &owner_label,
+                                            const std::string &clnt_label,
+                                            DBCrypto::DBOperationType access_type) const
+    {
+        // owner of the entry have all the permissions by default
+        // check if requesting client is the entry owner - if so, exit (permission granted)
+        if(owner_label == clnt_label)
+            return true;
+
+        // perform permissions DB query
+        std::string permission_string = this->getPermissionsForAliasAndLabel(alias, clnt_label);
+
+        // check if requested operation is in the permission string
+        LogDebug("pair <" << alias << "," << clnt_label << "> permission rights: \"" << permission_string << "\"");
+        if(permission_string.find(access_type) != std::string::npos)
+            return true;
+
+        return false;
+    }
+    bool    DBCrypto::rowAccessControlCheck(const DBRow & input_row,
+                                            const std::string &clnt_label,
+                                            DBCrypto::DBOperationType access_type) const
+    {
+        return this->rowAccessControlCheck(input_row.alias, input_row.smackLabel, clnt_label, access_type);
+    }
+
+
     DBCrypto::DBRowOptional DBCrypto::getDBRow(
         const Alias &alias,
-        const std::string &label,
+        const std::string &clnt_label,
         DBDataType type)
     {
         Try {
@@ -292,11 +363,21 @@ using namespace DB;
                     m_connection->PrepareDataCommand(select_alias_cmd);
             selectCommand->BindString(1, alias.c_str());
             selectCommand->BindInteger(2, static_cast<int>(type));
-            selectCommand->BindString(3, label.c_str());
 
-            if(selectCommand->Step()) {
+            if(selectCommand->Step())
+            {
+                // extract data
+                DBRow current_row = getRow(selectCommand);
+
+                // check access rights here
+                if( ! this->rowAccessControlCheck(current_row, clnt_label, DBCrypto::DB_OPERATION_READ) )
+                    ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested operation");
+
+                // finalize DB operations
                 transaction.commit();
-                return DBRowOptional(getRow(selectCommand));
+
+                // all okay, proceed
+                return DBRowOptional(current_row);
             } else {
                 return DBRowOptional();
             }
@@ -309,12 +390,12 @@ using namespace DB;
         }
         ThrowMsg(DBCrypto::Exception::InternalError,
                 "Couldn't get row for type " << static_cast<int>(type) <<
-                " alias " << alias << " and label " << label);
+                " alias " << alias << " using client label " << clnt_label);
     }
 
     DBCrypto::DBRowOptional DBCrypto::getKeyDBRow(
         const Alias &alias,
-        const std::string &label)
+        const std::string &clnt_label)
     {
         Try{
             Transaction transaction(this);
@@ -323,11 +404,21 @@ using namespace DB;
             selectCommand->BindString(1, alias.c_str());
             selectCommand->BindInteger(2, static_cast<int>(DBDataType::DB_KEY_FIRST));
             selectCommand->BindInteger(3, static_cast<int>(DBDataType::DB_KEY_LAST));
-            selectCommand->BindString(4, label.c_str());
 
-            if(selectCommand->Step()) {
+            if(selectCommand->Step())
+            {
+                // extract data
+                DBRow current_row = getRow(selectCommand);
+
+                // check access rights here
+                if( ! this->rowAccessControlCheck(current_row, clnt_label, DBCrypto::DB_OPERATION_READ) )
+                    ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested operation");
+
+                // finalize DB operations
                 transaction.commit();
-                return DBRowOptional(getRow(selectCommand));
+
+                // all okay, proceed
+                return DBRowOptional(current_row);
             } else {
                 return DBRowOptional();
             }
@@ -340,19 +431,17 @@ using namespace DB;
         }
         ThrowMsg(DBCrypto::Exception::InternalError,
                 "Couldn't get Key for alias " << alias
-                << " and label " << label);
+                << " using client label " << clnt_label);
     }
 
     void DBCrypto::getSingleType(
             DBDataType type,
-            const std::string& label,
-            AliasVector& aliases)
+            AliasVector& aliases) const
     {
         Try{
             SqlConnection::DataCommandUniquePtr selectCommand =
                             m_connection->PrepareDataCommand(select_type_cmd);
             selectCommand->BindInteger(1, static_cast<int>(type));
-            selectCommand->BindString(2, label.c_str());
 
             while(selectCommand->Step()) {
                 Alias alias;
@@ -368,22 +457,18 @@ using namespace DB;
             LogError("Couldn't execute select statement");
         }
         ThrowMsg(DBCrypto::Exception::InternalError,
-                "Couldn't get type " << static_cast<int>(type)
-                << " for label " << label);
+                "Couldn't get type " << static_cast<int>(type));
     }
 
     void DBCrypto::getAliases(
         DBDataType type,
-        const std::string& label,
         AliasVector& aliases)
     {
-        getSingleType(type, label, aliases);
+        getSingleType(type, aliases);
     }
 
 
-    void DBCrypto::getKeyAliases(
-        const std::string &label,
-        AliasVector &aliases)
+    void DBCrypto::getKeyAliases(AliasVector &aliases)
     {
         Try{
             Transaction transaction(this);
@@ -391,7 +476,6 @@ using namespace DB;
                             m_connection->PrepareDataCommand(select_key_type_cmd);
             selectCommand->BindInteger(1, static_cast<int>(DBDataType::DB_KEY_FIRST));
             selectCommand->BindInteger(2, static_cast<int>(DBDataType::DB_KEY_LAST));
-            selectCommand->BindString(3, label.c_str());
 
             while(selectCommand->Step()) {
                 Alias alias;
@@ -407,46 +491,43 @@ using namespace DB;
         } Catch (SqlConnection::Exception::InternalError) {
             LogError("Couldn't execute select statement");
         }
-        ThrowMsg(DBCrypto::Exception::InternalError,
-                "Couldn't get key aliases for label " << label);
+        ThrowMsg(DBCrypto::Exception::InternalError, "Couldn't get key aliases");
     }
 
-    int DBCrypto::countRows(const Alias &alias, const std::string &label) {
-        SqlConnection::DataCommandUniquePtr checkCmd =
-                    m_connection->PrepareDataCommand(select_count_rows_cmd);
-        checkCmd->BindString(1, alias.c_str());
-        checkCmd->BindString(2, label.c_str());
-        if(checkCmd->Step()) {
-            return checkCmd->GetColumnInteger(0);
-        } else {
-            LogDebug("Row does not exist for alias=" << alias << "and label=" << label);
-            return 0;
-        }
-    }
-    int DBCrypto::deleteDBRow(
-            const Alias &alias,
-            const std::string &label)
+    bool DBCrypto::deleteDBRow(const Alias &alias, const std::string &clnt_label)
     {
         Try {
             Transaction transaction(this);
-            unsigned int count;
-            if((count = countRows(alias, label)) > 0) {
+
+            std::string owner_label = getLabelForAlias(alias);
+            if( ! owner_label.empty() )
+            {
+                // check access rights here
+                if( ! this->rowAccessControlCheck(alias, owner_label, clnt_label, DBCrypto::DB_OPERATION_REMOVE) )
+                    ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested remove operation");
+
+                // if here, access right is granted - proceed with removal
+                // note: PERMISSION_TABLE entry will be deleted automatically by SQL (cascade relation between tables)
                 SqlConnection::DataCommandUniquePtr deleteCommand =
                         m_connection->PrepareDataCommand(delete_alias_cmd);
                 deleteCommand->BindString(1, alias.c_str());
-                deleteCommand->BindString(2, label.c_str());
                 deleteCommand->Step();
+
                 transaction.commit();
+                return true;
+            }
+            else
+            {
+                LogError("Error: no such alias: " << alias);
+                return false;
             }
-            return count;
         } Catch (SqlConnection::Exception::SyntaxError) {
             LogError("Couldn't prepare delete statement");
         } Catch (SqlConnection::Exception::InternalError) {
             LogError("Couldn't execute delete statement");
         }
         ThrowMsg(DBCrypto::Exception::InternalError,
-                "Couldn't delete DBRow for alias " << alias
-                << " and label " << label);
+                "Couldn't delete DBRow for alias " << alias << " using client label " << clnt_label);
     }
 
     void DBCrypto::saveKey(
@@ -525,6 +606,92 @@ using namespace DB;
                 "Couldn't delete key for label " << label);
     }
 
+
+    int DBCrypto::setAccessRights(  const std::string& clnt_label,
+                                    const Alias& alias,
+                                    const std::string& accessor_label,
+                                    const AccessRight value_to_set)
+    {
+        Try {
+            Transaction transaction(this);
+
+            // check if label is present
+            std::string owner_label = getLabelForAlias(alias);
+            if( ! owner_label.empty() )
+            {
+                // owner can not add permissions to itself
+                if(owner_label.compare(accessor_label) == 0)
+                    ThrowMsg(Exception::InvalidArgs, "Invalid accessor label: equal to owner label");
+
+                // check access rights here - only owner can modify permissions
+                if(owner_label != clnt_label)
+                    ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested write operation");
+
+                // if here, access right is granted - proceed to set permissions
+                SqlConnection::DataCommandUniquePtr setPermissionCommand =
+                        m_connection->PrepareDataCommand(set_permission_alias_cmd);
+                setPermissionCommand->BindString(1, alias.c_str());
+                setPermissionCommand->BindString(2, accessor_label.c_str());
+                setPermissionCommand->BindString(3, toDBAccessRight(value_to_set));
+                setPermissionCommand->Step();
+                transaction.commit();
+                return CKM_API_SUCCESS;
+            }
+            else
+            {
+                LogError("Error: no such alias: " << alias);
+                return CKM_API_ERROR_DB_ALIAS_UNKNOWN;
+            }
+        } Catch (SqlConnection::Exception::SyntaxError) {
+            LogError("Couldn't prepare set statement");
+        } Catch (SqlConnection::Exception::InternalError) {
+            LogError("Couldn't execute set statement");
+        }
+        ThrowMsg(DBCrypto::Exception::InternalError,
+                "Couldn't set permissions for alias " << alias << " using client label " << clnt_label);
+    }
+
+    int DBCrypto::clearAccessRights(const std::string& clnt_label,
+                                    const Alias& alias,
+                                    const std::string& accessor_label)
+    {
+        Try {
+            Transaction transaction(this);
+
+            std::string owner_label = getLabelForAlias(alias);
+            if( ! owner_label.empty() )
+            {
+                // check access rights here - only owner can modify permissions
+                if(owner_label != clnt_label)
+                    ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested write operation");
+
+                // check if permission for <label, accessor_label> is defined - otherwise nothing to drop
+                if( this->getPermissionsForAliasAndLabel(alias, accessor_label).empty() )
+                    ThrowMsg(Exception::InvalidArgs, "Permission not found");
+
+                // if here, access right is granted - proceed to delete permissions
+                SqlConnection::DataCommandUniquePtr deletePermissionCommand =
+                        m_connection->PrepareDataCommand(delete_permission_cmd);
+                deletePermissionCommand->BindString(1, alias.c_str());
+                deletePermissionCommand->BindString(2, accessor_label.c_str());
+                deletePermissionCommand->Step();
+                transaction.commit();
+                return CKM_API_SUCCESS;
+            }
+            else
+            {
+                LogError("Error: no such alias: " << alias);
+                return CKM_API_ERROR_DB_ALIAS_UNKNOWN;
+            }
+        } Catch (SqlConnection::Exception::SyntaxError) {
+            LogError("Couldn't prepare delete statement");
+        } Catch (SqlConnection::Exception::InternalError) {
+            LogError("Couldn't execute delete statement");
+        }
+        ThrowMsg(DBCrypto::Exception::InternalError,
+                "Couldn't delete permissions for alias " << alias << " using client label " << clnt_label);
+    }
+
 } // CKM
 
 #pragma GCC diagnostic pop
index 3153974..ab35a03 100644 (file)
@@ -47,6 +47,8 @@ namespace CKM {
                 DECLARE_EXCEPTION_TYPE(Base, AliasExists)
                 DECLARE_EXCEPTION_TYPE(Base, InternalError)
                 DECLARE_EXCEPTION_TYPE(Base, TransactionError)
+                DECLARE_EXCEPTION_TYPE(Base, PermissionDenied)
+                DECLARE_EXCEPTION_TYPE(Base, InvalidArgs)
             };
             DBCrypto() :
                 m_connection(NULL),
@@ -65,26 +67,34 @@ namespace CKM {
             void saveDBRow(const DBRow &row);
             DBRowOptional getDBRow(
                     const Alias &alias,
-                    const std::string &label,
+                    const std::string &clnt_label,
                     DBDataType type);
             DBRowOptional getKeyDBRow(
                     const Alias &alias,
-                    const std::string &label);
+                    const std::string &clnt_label);
             void getAliases(
                     DBDataType dataType,
-                    const std::string &label,
                     AliasVector &aliases);
-            void getKeyAliases(
-                    const std::string &label,
-                    AliasVector &aliases);
-            int deleteDBRow(
+            void getKeyAliases(AliasVector &aliases);
+            bool deleteDBRow(
                     const Alias& alias,
-                    const std::string &label);
+                    const std::string &clnt_label);
+
+            // keys
             void saveKey(const std::string& label, const RawBuffer &key);
             RawBufferOptional getKey(
                     const std::string& label);
             void deleteKey(const std::string& label);
 
+            // permissions
+            int setAccessRights(const std::string& clnt_label,
+                                const Alias& alias,
+                                const std::string& accessor_label,
+                                const AccessRight value_to_set);
+            int clearAccessRights(const std::string& clnt_label,
+                                  const Alias& alias,
+                                  const std::string& accessor_label);
+
             int beginTransaction();
             int commitTransaction();
             int rollbackTransaction();
@@ -162,20 +172,42 @@ namespace CKM {
 
             void initDatabase();
             DBRow getRow(const DB::SqlConnection::DataCommandUniquePtr &selectCommand);
+
+            enum DBOperationType : char {
+                DB_OPERATION_READ       = 'R',            ///< read DB row
+                DB_OPERATION_WRITE      = 'W',            ///< modify DB row
+                DB_OPERATION_REMOVE     = 'D'             ///< delete DB row
+            };
+
+            /**
+             * read PERMISSION_TABLE entry for pair <alias, label>
+             *
+             * @return permission string (consisting with DBOperationType chars), may be empty
+             */
+            std::string getPermissionsForAliasAndLabel(const Alias &alias, const std::string &label) const;
+
+            /**
+             * check if current requesting alias has permissions
+             * to perform operation on the given DB entry
+             *
+             * @return true if permission granted, false otherwise
+             */
+            bool  rowAccessControlCheck(const DBRow & input_row,
+                                        const std::string &clnt_label,
+                                        DBOperationType access_type) const;
+            // helper
+            bool  rowAccessControlCheck(const Alias &alias,
+                                        const std::string &owner_label,
+                                        const std::string &clnt_label,
+                                        DBOperationType access_type) const;
+
             void createTable(
                     const char *create_cmd,
                     const char *table_name);
-            bool checkAliasExist(
-                    const std::string &alias,
-                    const std::string &label);
-            bool checkGlobalAliasExist(const std::string& alias);
-            int countRows(
-                    const std::string &alias,
-                    const std::string &label);
-            void getSingleType(
-                    DBDataType type,
-                    const std::string& label,
-                    AliasVector& aliases);
+            bool checkAliasExist(const std::string &alias) const;
+            std::string getLabelForAlias(const std::string& alias) const;
+            bool checkGlobalAliasExist(const std::string& alias) const;
+            void getSingleType(DBDataType type, AliasVector& aliases) const;
    };
 } // namespace CKM
 
index c4a452f..e15fa44 100644 (file)
@@ -3,6 +3,7 @@
 #include <unistd.h>
 #include <db-crypto.h>
 #include <iostream>
+#include <ckm/ckm-type.h>
 #include <ckm/ckm-error.h>
 #include <errno.h>
 
@@ -12,6 +13,9 @@ BOOST_GLOBAL_FIXTURE(TestConfig)
 
 using namespace CKM;
 
+namespace
+{
+
 const char* default_alias = "alias";
 const char* default_label = "label";
 
@@ -20,10 +24,31 @@ const char* crypto_db = "/tmp/testme.db";
 const int restricted_local = 1;
 const int restricted_global = 0;
 
-DBRow createDefaultRow(DBDataType type = DBDataType::BINARY_DATA) {
+// mirrors the API-defined value
+#define AES_GCM_TAG_SIZE 16
+
+const char *row_A_alias = "row_A_alias";
+const char *row_A_label = "app_A";
+
+const char *row_B_alias = "row_B_alias";
+const char *row_B_label = "app_B";
+
+const char *row_C_alias = "row_C_alias";
+const char *row_C_label = "app_C";
+
+void initDB(DBCrypto & db)
+{
+    BOOST_CHECK(unlink(crypto_db) == 0 || errno == ENOENT);
+    BOOST_REQUIRE_NO_THROW(db = DBCrypto(crypto_db, defaultPass));
+}
+
+DBRow createDefaultRow( DBDataType type = DBDataType::BINARY_DATA,
+                        const char *optional_alias = NULL,
+                        const char *optional_label = NULL)
+{
     DBRow row;
-    row.alias = default_alias;
-    row.smackLabel = default_label;
+    row.alias = optional_alias?optional_alias:default_alias;
+    row.smackLabel = optional_label?optional_label:default_label;
     row.exportable = 1;
     row.algorithmType = DBCMAlgType::AES_GCM_256;
     row.dataType = type;
@@ -80,41 +105,77 @@ void checkDBIntegrity(const DBRow &rowPattern, DBCrypto &db) {
     BOOST_REQUIRE_MESSAGE(!row_optional, "Select should not return row after deletion");
 }
 
+void insertRow(DBCrypto & db, const char *alias, const char *label)
+{
+    DBRow rowPattern = createDefaultRow(DBDataType::BINARY_DATA, alias, label);
+    rowPattern.data = RawBuffer(100, 20);
+    rowPattern.dataSize = rowPattern.data.size();
+    rowPattern.tag = RawBuffer(AES_GCM_TAG_SIZE, 1);
+    BOOST_REQUIRE_NO_THROW(db.saveDBRow(rowPattern));
+}
+
+void deleteRow(DBCrypto & db, const char *alias, const char *label)
+{
+    bool exit_flag;
+    BOOST_REQUIRE_NO_THROW(exit_flag = db.deleteDBRow(alias, label));
+    BOOST_REQUIRE_MESSAGE(true == exit_flag, "remove alias failed: no rows removed");
+}
+
+void addPermission(DBCrypto & db, const char *alias, const char *owner_label, const char *accessor_label)
+{
+    int ec;
+    BOOST_REQUIRE_NO_THROW(ec = db.setAccessRights(std::string(owner_label),
+                                                   std::string(alias),
+                                                   std::string(accessor_label),
+                                                   CKM::AccessRight::AR_READ_REMOVE));
+    BOOST_REQUIRE_MESSAGE(CKM_API_SUCCESS == ec, "add permission failed: " << ec);
+}
+
+void readRowExpectFail(DBCrypto & db, const char *alias, const char *accessor_label)
+{
+    DBCrypto::DBRowOptional row;
+    BOOST_REQUIRE_THROW(row = db.getDBRow(alias, accessor_label, DBDataType::BINARY_DATA), DBCrypto::Exception::PermissionDenied);
+}
+
+void readRowExpectSuccess(DBCrypto & db, const char *alias, const char *accessor_label)
+{
+    DBCrypto::DBRowOptional row;
+    BOOST_REQUIRE_NO_THROW(row = db.getDBRow(alias, accessor_label, DBDataType::BINARY_DATA));
+    BOOST_REQUIRE_MESSAGE(row, "row is empty");
+}
+}
+
 BOOST_AUTO_TEST_SUITE(DBCRYPTO_TEST)
 BOOST_AUTO_TEST_CASE(DBtestSimple) {
-
-    BOOST_CHECK(unlink(crypto_db) == 0 || errno == ENOENT);
     DBCrypto db;
-    BOOST_REQUIRE_NO_THROW(db = DBCrypto(crypto_db, defaultPass));
+    initDB(db);
 
     DBRow rowPattern = createDefaultRow();
     rowPattern.data = RawBuffer(32, 1);
     rowPattern.dataSize = rowPattern.data.size();
-    rowPattern.tag = RawBuffer(16, 1);
+    rowPattern.tag = RawBuffer(AES_GCM_TAG_SIZE, 1);
 
     checkDBIntegrity(rowPattern, db);
 }
 BOOST_AUTO_TEST_CASE(DBtestBIG) {
-    BOOST_CHECK(unlink(crypto_db) == 0 || errno == ENOENT);
     DBCrypto db;
-    BOOST_REQUIRE_NO_THROW(db = DBCrypto(crypto_db, defaultPass));
+    initDB(db);
 
     DBRow rowPattern = createDefaultRow();
     rowPattern.data = createBigBlob(4096);
     rowPattern.dataSize = rowPattern.data.size();
-    rowPattern.tag = RawBuffer(16, 1);
+    rowPattern.tag = RawBuffer(AES_GCM_TAG_SIZE, 1);
 
     checkDBIntegrity(rowPattern, db);
 }
 BOOST_AUTO_TEST_CASE(DBtestGlobal) {
-    BOOST_CHECK(unlink(crypto_db) == 0 || errno == ENOENT);
     DBCrypto db;
-    BOOST_REQUIRE_NO_THROW(db = DBCrypto(crypto_db, defaultPass));
+    initDB(db);
 
     DBRow rowPattern = createDefaultRow();
     rowPattern.data = RawBuffer(1024, 2);
     rowPattern.dataSize = rowPattern.data.size();
-    rowPattern.tag = RawBuffer(16, 1);
+    rowPattern.tag = RawBuffer(AES_GCM_TAG_SIZE, 1);
 
     BOOST_REQUIRE_NO_THROW(db.saveDBRow(rowPattern));
 
@@ -125,14 +186,13 @@ BOOST_AUTO_TEST_CASE(DBtestGlobal) {
             DBCrypto::Exception::AliasExists);
 }
 BOOST_AUTO_TEST_CASE(DBtestTransaction) {
-    BOOST_CHECK(unlink(crypto_db) == 0 || errno == ENOENT);
     DBCrypto db;
-    BOOST_REQUIRE_NO_THROW(db = DBCrypto(crypto_db, defaultPass));
+    initDB(db);
 
     DBRow rowPattern = createDefaultRow();
     rowPattern.data = RawBuffer(100, 20);
     rowPattern.dataSize = rowPattern.data.size();
-    rowPattern.tag = RawBuffer(16, 1);
+    rowPattern.tag = RawBuffer(AES_GCM_TAG_SIZE, 1);
     DBCrypto::Transaction transaction(&db);
 
     BOOST_REQUIRE_NO_THROW(db.saveDBRow(rowPattern));
@@ -144,4 +204,87 @@ BOOST_AUTO_TEST_CASE(DBtestTransaction) {
     BOOST_CHECK_MESSAGE(!row_optional, "Row still present after rollback");
 
 }
+
+BOOST_AUTO_TEST_CASE(DBaddDataCheckIfPermissionIsAdded)
+{
+    DBCrypto db;
+    initDB(db);
+
+    // insert initial data set
+    insertRow(db, row_A_alias, row_A_label);
+    insertRow(db, row_B_alias, row_B_label);
+    readRowExpectSuccess(db, row_A_alias, row_A_label);
+    readRowExpectSuccess(db, row_B_alias, row_B_label);
+
+    // verify that no entries present in the permission table
+    // read row A from label B and vice versa
+    readRowExpectFail(db, row_A_alias, row_B_label);
+    readRowExpectFail(db, row_B_alias, row_A_label);
+
+    // add appropriate permissions for label B
+    addPermission(db, row_A_alias, row_A_label, row_B_label);
+
+    // B should have access to A, while A should not to B
+    // read row A from label B and vice versa
+    readRowExpectSuccess(db, row_A_alias, row_B_label);
+    readRowExpectFail(db, row_B_alias, row_A_label);
+
+    // add appropriate permissions for label A
+    addPermission(db, row_B_alias, row_B_label, row_A_label);
+
+    // B should have access to A, same as A have access to B
+    // read row A from label B and vice versa
+    readRowExpectSuccess(db, row_A_alias, row_B_label);
+    readRowExpectSuccess(db, row_B_alias, row_A_label);
+}
+
+
+BOOST_AUTO_TEST_CASE(DBremoveDataCheckIfPermissionIsRemoved)
+{
+    DBCrypto db;
+    initDB(db);
+
+    // insert initial data set
+    insertRow(db, row_A_alias, row_A_label);
+    insertRow(db, row_B_alias, row_B_label);
+    insertRow(db, row_C_alias, row_C_label);
+    addPermission(db, row_A_alias, row_A_label, row_B_label);
+    addPermission(db, row_B_alias, row_B_label, row_A_label);
+    // to test multiple permissions removal
+    // put intentionally after row_B_alias permission entry
+    addPermission(db, row_A_alias, row_A_label, row_C_label);
+
+    // B should have access to A, same as A have access to B
+    // read row A from label B and vice versa
+    readRowExpectSuccess(db, row_A_alias, row_B_label);
+    readRowExpectSuccess(db, row_A_alias, row_C_label);
+    readRowExpectSuccess(db, row_B_alias, row_A_label);
+    readRowExpectFail(db, row_B_alias, row_C_label);
+
+    // remove data A - expect permissions for B and C to be removed as well
+    deleteRow(db, row_A_alias, row_A_label);
+    // insert it again - expect permissions for label B and C not to be there anymore
+    insertRow(db, row_A_alias, row_A_label);
+
+    // read row A from label B and vice versa
+    readRowExpectFail(db, row_A_alias, row_B_label);
+    readRowExpectFail(db, row_A_alias, row_C_label);
+    readRowExpectSuccess(db, row_B_alias, row_A_label);
+
+    // remove data B - expect permission to be removed as well
+    deleteRow(db, row_B_alias, row_B_label);
+    // insert it again - expect permissions for label A not to be there anymore
+    insertRow(db, row_B_alias, row_B_label);
+
+    // read row A from label B and vice versa
+    readRowExpectFail(db, row_A_alias, row_B_label);
+    readRowExpectFail(db, row_A_alias, row_C_label);
+    readRowExpectFail(db, row_B_alias, row_A_label);
+
+    // sanity check: data exists
+    readRowExpectSuccess(db, row_A_alias, row_A_label);
+    readRowExpectSuccess(db, row_B_alias, row_B_label);
+}
+
+
 BOOST_AUTO_TEST_SUITE_END()