X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fmanager%2Fservice%2Fdb-crypto.cpp;h=ce9d750db0c8536d0f180164897e795d5ee9723e;hb=4b4f7b9e045fadc3c5348e7ef8be628a742907e2;hp=daced4267cba01fbc0b5a5c1a97d891987ce4d72;hpb=6d6ddcc74076f556a810f4ced007c26d302d9446;p=platform%2Fcore%2Fsecurity%2Fkey-manager.git diff --git a/src/manager/service/db-crypto.cpp b/src/manager/service/db-crypto.cpp index daced42..ce9d750 100644 --- a/src/manager/service/db-crypto.cpp +++ b/src/manager/service/db-crypto.cpp @@ -20,6 +20,8 @@ * @brief Implementation of encrypted db access layer */ +#include +#include #include #include #include @@ -29,136 +31,149 @@ #pragma GCC diagnostic warning "-Wdeprecated-declarations" namespace { - const char *main_table = "CKM_TABLE"; - const char *key_table = "KEY_TABLE"; - -// CKM_TABLE (alias TEXT, label TEXT, restricted INT, exportable INT, dataType INT, -// algorithmType INT, encryptionScheme INT, iv BLOB, dataSize INT, data BLOB) - - const char *db_create_main_cmd = - "CREATE TABLE CKM_TABLE(" - " alias TEXT NOT NULL," - " label TEXT NOT NULL," - " restricted INTEGER NOT NULL," - " exportable INTEGER NOT NULL," - " dataType INTEGER NOT NULL," - " algorithmType INTEGER NOT NULL," - " encryptionScheme INTEGER NOT NULL," - " iv BLOB NOT NULL," - " dataSize INTEGER NOT NULL," - " data BLOB NOT NULL," - " PRIMARY KEY(alias, label)," - " UNIQUE(alias, restricted)" - ");"; - - - const char *insert_main_cmd = - "INSERT INTO CKM_TABLE(" - // 1 2 3 4 - " alias, label, restricted, exportable," - // 5 6 7 - " dataType, algorithmType, encryptionScheme," - // 8 9 10 - " iv, dataSize, data) " - "VALUES(" - " ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"; - - const char *select_alias_cmd = - // 1 2 3 - "SELECT * FROM CKM_TABLE WHERE alias=? AND dataType=? AND restricted=1 AND label=? " - " UNION ALL " - // 4 5 - " SELECT * FROM CKM_TABLE WHERE alias=? AND dataType=? AND restricted=0;"; - - const char *select_check_alias_cmd = - // 1 2 - "SELECT dataType FROM CKM_TABLE WHERE alias=? AND label=? AND restricted=1; "; - - const char *select_check_global_alias_cmd = - // 1 - "SELECT label FROM CKM_TABLE WHERE alias=? AND restricted=0;"; - - const char *select_key_alias_cmd = - // 1 - "SELECT * FROM CKM_TABLE WHERE alias=?" - // 2 3 - " AND dataType BETWEEN ? AND ? " - // 4 - " AND (restricted=0 OR label=?);"; - - const char *select_key_type_cmd = - "SELECT alias FROM CKM_TABLE WHERE " - // 1 - " dataType >= ? AND " - // 2 - " dataType <= ? AND " - // 3 - " (restricted=0 OR label=?)"; - - const char *select_type_cmd = - // 1 - "SELECT alias FROM CKM_TABLE WHERE dataType=? AND restricted=0 " - "UNION ALL " - // 2 3 - "SELECT alias FROM CKM_TABLE WHERE dataType=? AND restricted=1 AND label=?;"; - - const char *delete_alias_cmd = - // 1 2 - "DELETE FROM CKM_TABLE WHERE alias=? AND label=?;"; - -// KEY_TABLE (label TEXT, key BLOB) - - const char *db_create_key_cmd = - "CREATE TABLE KEY_TABLE(" - " label TEXT PRIMARY KEY," - " key BLOB NOT NULL" - ");"; - - const char *insert_key_cmd = - "INSERT INTO KEY_TABLE(label, key) VALUES (?, ?);"; - const char *select_key_cmd = - "SELECT key FROM KEY_TABLE WHERE label=?;"; - const char *delete_key_cmd = - "DELETE FROM KEY_TABLE WHERE label=?"; + const CKM::PermissionMask DEFAULT_PERMISSIONS = + static_cast(CKM::Permission::READ | CKM::Permission::REMOVE); + + const char *SCRIPTS_PATH = "/usr/share/ckm/scripts/"; + + enum DBVersion : int { + DB_VERSION_1 = 1, + DB_VERSION_2 = 2, + /* ... since version 3, there is no need to manually + * recognize database version. + * Remember only that if doing changes to the database, + * increment and update DB_VERSION_CURRENT, + * then provide migration mechanism! + */ + DB_VERSION_CURRENT = 4 + }; + + const char *SCRIPT_CREATE_SCHEMA = "create_schema"; + const char *SCRIPT_DROP_ALL_ITEMS = "drop_all"; + const char *SCRIPT_MIGRATE = "migrate_"; + + // common substitutions: + // 100 - idx + // 101 - name + // 102 - label + // 103 - value + // 104 - permissionLabel + // 105 - permissionMask + const char *DB_CMD_SCHEMA_SET = + "REPLACE INTO SCHEMA_INFO(name, value) " + " VALUES(?101, ?103);"; + + const char *DB_CMD_SCHEMA_GET = + "SELECT * FROM SCHEMA_INFO WHERE name=?101;"; + + const char *DB_SCHEMA_VERSION_FIELD = "schema_version"; + + + const char *DB_CMD_NAME_INSERT = + "INSERT INTO NAMES(" + " name, label) " + " VALUES(?101, ?102);"; + + const char *DB_CMD_NAME_COUNT_ROWS = + "SELECT COUNT(idx) FROM NAMES WHERE name=?101 AND label=?102;"; + + const char *DB_CMD_NAME_DELETE = + "DELETE FROM NAMES WHERE name=?101 AND label=?102;"; + + const char *DB_CMD_NAME_DELETE_BY_LABEL = + "DELETE FROM NAMES WHERE label=?102;"; + + + const char *DB_CMD_OBJECT_INSERT = + "INSERT INTO OBJECTS(" + " exportable, dataType," + " algorithmType, encryptionScheme," + " iv, dataSize, data, tag, idx, backendId) " + " VALUES(?001, ?002, ?003, ?004, ?005, " + " ?006, ?007, ?008," + " (SELECT idx FROM NAMES WHERE name=?101 and label=?102)," + " ?009" + " );"; + + const char *DB_CMD_OBJECT_SELECT_BY_NAME_AND_LABEL = + "SELECT * FROM [join_name_object_tables] " + " WHERE (dataType BETWEEN ?001 AND ?002) " + " AND name=?101 and label=?102;"; + + + const char *DB_CMD_KEY_INSERT = + "INSERT INTO KEYS(label, key) VALUES (?, ?);"; + const char *DB_CMD_KEY_SELECT = + "SELECT key FROM KEYS WHERE label=?;"; + const char *DB_CMD_KEY_DELETE = + "DELETE FROM KEYS WHERE label=?"; + + + const char *DB_CMD_PERMISSION_SET = // SQLite does not support updating views + "REPLACE INTO PERMISSIONS(permissionLabel, permissionMask, idx) " + " VALUES (?104, ?105, (SELECT idx FROM NAMES WHERE name=?101 and label=?102));"; + + const char *DB_CMD_PERMISSION_SELECT = + "SELECT permissionMask FROM [join_name_permission_tables] " + " WHERE permissionLabel=?104 " + " AND name=?101 and label=?102;"; + + const char *DB_CMD_PERMISSION_DELETE = // SQLite does not support updating views + "DELETE FROM PERMISSIONS WHERE permissionLabel=?104 AND " + " idx=(SELECT idx FROM NAMES WHERE name=?101 and label=?102);"; + + + /* + * GROUP BY is necessary because of the following case: + * -There are several permissions to L1, N1 (label, name) from other accessors. When listing + * objects accessible by L1 the query will produce one result (L1, N1) for each allowed + * accessor but GROUP BY will reduce them to one so L1 will have (L1, N1) on its list only once + */ + const char *DB_CMD_NAME_SELECT_BY_TYPE_AND_PERMISSION = + "SELECT label, name FROM [join_all_tables] " + " WHERE dataType>=?001 AND dataType<=?002 " + " AND permissionLabel=?104 AND permissionMask&?004!=0 GROUP BY idx;"; } namespace CKM { -using namespace DB; - DBCrypto::DBCrypto(const std::string& path, - const RawBuffer &rawPass) { +namespace DB { + Crypto::Crypto(const std::string& path, const RawBuffer &rawPass) + { m_connection = NULL; m_inUserTransaction = false; Try { m_connection = new SqlConnection(path, SqlConnection::Flag::Option::CRW); m_connection->SetKey(rawPass); initDatabase(); + m_connection->ExecCommand("VACUUM;"); } Catch(SqlConnection::Exception::ConnectionBroken) { LogError("Couldn't connect to database: " << path); - ReThrow(DBCrypto::Exception::InternalError); + ReThrow(Crypto::Exception::InternalError); } Catch(SqlConnection::Exception::InvalidArguments) { LogError("Couldn't set the key for database"); - ReThrow(DBCrypto::Exception::InternalError); + ReThrow(Crypto::Exception::InternalError); } Catch(SqlConnection::Exception::SyntaxError) { LogError("Couldn't initiate the database"); - ReThrow(DBCrypto::Exception::InternalError); + ReThrow(Crypto::Exception::InternalError); } Catch(SqlConnection::Exception::InternalError) { LogError("Couldn't create the database"); - ReThrow(DBCrypto::Exception::InternalError); + ReThrow(Crypto::Exception::InternalError); } } - DBCrypto::DBCrypto(DBCrypto &&other) : + Crypto::Crypto(Crypto &&other) : m_connection(other.m_connection), - m_inUserTransaction(other.m_inUserTransaction){ + m_inUserTransaction(other.m_inUserTransaction) + { other.m_connection = NULL; other.m_inUserTransaction = false; } - DBCrypto::~DBCrypto() { + Crypto::~Crypto() { delete m_connection; } - DBCrypto& DBCrypto::operator=(DBCrypto&& other) { + Crypto& Crypto::operator=(Crypto&& other) { if (this == &other) return *this; delete m_connection; @@ -172,7 +187,7 @@ using namespace DB; return *this; } - void DBCrypto::createTable( + void Crypto::createTable( const char* create_cmd, const char *table_name) { @@ -181,122 +196,246 @@ using namespace DB; } Catch(SqlConnection::Exception::SyntaxError) { LogError("Couldn't create table : " << table_name << "!"); throw; + } Catch(SqlConnection::Exception::InternalError) { + LogError("Sqlite got into infinite busy state"); + throw; } } - void DBCrypto::initDatabase() { - Transaction transaction(this); - if(!m_connection->CheckTableExist(main_table)) { - createTable(db_create_main_cmd, main_table); - } - if(!m_connection->CheckTableExist(key_table)) { - createTable(db_create_key_cmd, key_table); + void Crypto::createView( + const char* create_cmd) + { + Try { + m_connection->ExecCommand(create_cmd); + } Catch(SqlConnection::Exception::SyntaxError) { + LogError("Couldn't create view!"); + throw; + } Catch(SqlConnection::Exception::InternalError) { + LogError("Sqlite got into infinite busy state"); + throw; } - transaction.commit(); } - bool DBCrypto::checkGlobalAliasExist(const std::string& alias) { - 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)); + bool Crypto::getDBVersion(int & schemaVersion) + { + SchemaInfo SchemaInfo(this); + if(SchemaInfo.getVersionInfo(schemaVersion)) { + LogDebug("Current DB version: " << schemaVersion); return true; - } else - return false; - } + } + else + { + LogDebug("No DB version known or DB not present"); + + // special case: old CKM_TABLE exists + if(m_connection->CheckTableExist("CKM_TABLE")) { + schemaVersion = DB_VERSION_1; + return true; + } - bool DBCrypto::checkAliasExist( - const std::string& alias, - const std::string& label) { - 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)); - return true; - } else - return false; + // special case: new scheme exists, but no SCHEMA_INFO table present + else if(m_connection->CheckTableExist("NAME_TABLE")) { + schemaVersion = DB_VERSION_2; + return true; + } + } + // not recognized - proceed with an empty DBs + return false; } - void DBCrypto::saveDBRow(const DBRow &row){ - Try { - - //Sqlite does not support partial index in our version, - //so we do it by hand + void Crypto::initDatabase() + { + // run migration if old database is present + int schemaVersion; + if( getDBVersion(schemaVersion)==false || // DB empty or corrupted + schemaVersion > DB_VERSION_CURRENT) // or too new scheme + { + LogDebug("no database or database corrupted, initializing the DB"); + resetDB(); + } + else + { + // migration needed + LogDebug("DB migration from version " << schemaVersion << " to version " << DB_VERSION_CURRENT << " started."); Transaction transaction(this); - if((row.restricted == 1 && checkAliasExist(row.alias, row.smackLabel)) || - (row.restricted == 0 && checkGlobalAliasExist(row.alias))) { - ThrowMsg(DBCrypto::Exception::AliasExists, - "Alias exists for alias: " << row.alias - << ", label: " << row.smackLabel); + for(int vi=schemaVersion; viExecCommand((*script).c_str()); } + // update DB version info + SchemaInfo SchemaInfo(this); + SchemaInfo.setVersionInfo(); + transaction.commit(); + } + } - SqlConnection::DataCommandUniquePtr insertCommand = - m_connection->PrepareDataCommand(insert_main_cmd); - insertCommand->BindString(1, row.alias.c_str()); - insertCommand->BindString(2, row.smackLabel.c_str()); - insertCommand->BindInteger(3, row.restricted); - insertCommand->BindInteger(4, row.exportable); - insertCommand->BindInteger(5, static_cast(row.dataType)); - insertCommand->BindInteger(6, static_cast(row.algorithmType)); - insertCommand->BindInteger(7, row.encryptionScheme); - insertCommand->BindBlob(8, row.iv); - insertCommand->BindInteger(9, row.dataSize); - insertCommand->BindBlob(10, row.data); + Crypto::ScriptOptional Crypto::getScript(const std::string &scriptName) const + { + std::string scriptPath = SCRIPTS_PATH + scriptName + std::string(".sql"); + std::ifstream is(scriptPath); + if(is.fail()) { + LogError("Script " << scriptPath << " not found!"); + return ScriptOptional(); + } - insertCommand->Step(); - transaction.commit(); - return; + std::istreambuf_iterator begin(is),end; + return ScriptOptional(std::string(begin, end)); + } + + Crypto::ScriptOptional Crypto::getMigrationScript(int db_version) const + { + std::string scriptPath = std::string(SCRIPT_MIGRATE) + std::to_string(db_version); + return getScript(scriptPath); + } + + void Crypto::createDBSchema() { + Transaction transaction(this); + ScriptOptional script = getScript(SCRIPT_CREATE_SCHEMA); + if(!script) + { + std::string errmsg = "Can not create the database schema: no initialization script"; + LogError(errmsg); + ThrowMsg(Exception::InternalError, errmsg); + } + + m_connection->ExecCommand((*script).c_str()); + SchemaInfo SchemaInfo(this); + SchemaInfo.setVersionInfo(); + transaction.commit(); + } + + void Crypto::resetDB() { + Transaction transaction(this); + ScriptOptional script = getScript(SCRIPT_DROP_ALL_ITEMS); + if(!script) + { + std::string errmsg = "Can not clear the database: no clearing script"; + LogError(errmsg); + ThrowMsg(Exception::InternalError, errmsg); + } + + m_connection->ExecCommand((*script).c_str()); + createDBSchema(); + transaction.commit(); + } + + bool Crypto::isNameLabelPresent(const Name &name, const Label &owner) const { + Try { + NameTable nameTable(this->m_connection); + return nameTable.isPresent(name, owner); } Catch(SqlConnection::Exception::SyntaxError) { LogError("Couldn't prepare insert statement"); } Catch(SqlConnection::Exception::InternalError) { LogError("Couldn't execute insert statement"); } - ThrowMsg(DBCrypto::Exception::InternalError, - "Couldn't save DBRow"); - } - - DBRow DBCrypto::getRow(const SqlConnection::DataCommandUniquePtr &selectCommand) { - DBRow row; - row.alias = selectCommand->GetColumnString(0); - row.smackLabel = selectCommand->GetColumnString(1); - row.restricted = selectCommand->GetColumnInteger(2); - row.exportable = selectCommand->GetColumnInteger(3); - row.dataType = static_cast(selectCommand->GetColumnInteger(4)); - row.algorithmType = static_cast(selectCommand->GetColumnInteger(5)); - row.encryptionScheme = selectCommand->GetColumnInteger(6); - row.iv = selectCommand->GetColumnBlob(7); - row.dataSize = selectCommand->GetColumnInteger(8); - row.data = selectCommand->GetColumnBlob(9); - return row; + ThrowMsg(Crypto::Exception::InternalError, + "Couldn't check if name and label pair is present"); } - DBCrypto::DBRowOptional DBCrypto::getDBRow( - const Alias &alias, - const std::string &label, - DBDataType type) + void Crypto::saveRows(const Name &name, const Label &owner, const RowVector &rows) { Try { - Transaction transaction(this); - SqlConnection::DataCommandUniquePtr selectCommand = - m_connection->PrepareDataCommand(select_alias_cmd); - selectCommand->BindString(1, alias.c_str()); - selectCommand->BindInteger(2, static_cast(type)); - selectCommand->BindString(3, label.c_str()); - selectCommand->BindString(4, alias.c_str()); - selectCommand->BindInteger(5, static_cast(type)); + // transaction is present in the layer above + NameTable nameTable(this->m_connection); + ObjectTable objectTable(this->m_connection); + PermissionTable permissionTable(this->m_connection); + nameTable.addRow(name, owner); + for (const auto &i: rows) + objectTable.addRow(i); + permissionTable.setPermission(name, + owner, + owner, + static_cast(DEFAULT_PERMISSIONS)); + return; + } Catch(SqlConnection::Exception::SyntaxError) { + LogError("Couldn't prepare insert statement"); + } Catch(SqlConnection::Exception::InternalError) { + LogError("Couldn't execute insert statement: " << _rethrown_exception.GetMessage()); + } + ThrowMsg(Crypto::Exception::InternalError, + "Couldn't save Row"); + } - if(selectCommand->Step()) { - transaction.commit(); - return DBRowOptional(getRow(selectCommand)); - } else { - return DBRowOptional(); + void Crypto::saveRow(const Row &row) { + Try { + // transaction is present in the layer above + NameTable nameTable(this->m_connection); + ObjectTable objectTable(this->m_connection); + PermissionTable permissionTable(this->m_connection); + nameTable.addRow(row.name, row.ownerLabel); + objectTable.addRow(row); + permissionTable.setPermission(row.name, + row.ownerLabel, + row.ownerLabel, + static_cast(DEFAULT_PERMISSIONS)); + return; + } Catch(SqlConnection::Exception::SyntaxError) { + LogError("Couldn't prepare insert statement"); + } Catch(SqlConnection::Exception::InternalError) { + LogError("Couldn't execute insert statement"); + } + ThrowMsg(Crypto::Exception::InternalError, + "Couldn't save Row"); + } + + bool Crypto::deleteRow( + const Name &name, + const Label &ownerLabel) + { + Try { + // transaction is present in the layer above + NameTable nameTable(this->m_connection); + if(nameTable.isPresent(name, ownerLabel)) + { + nameTable.deleteRow(name, ownerLabel); + return true; } + return false; + } Catch (SqlConnection::Exception::SyntaxError) { + LogError("Couldn't prepare delete statement"); + } Catch (SqlConnection::Exception::InternalError) { + LogError("Couldn't execute delete statement"); + } + ThrowMsg(Crypto::Exception::InternalError, + "Couldn't delete Row for name " << name << " using ownerLabel " << ownerLabel); + } + + Row Crypto::getRow( + const SqlConnection::DataCommandUniquePtr &selectCommand) const { + Row row; + row.name = selectCommand->GetColumnString(0); + row.ownerLabel = selectCommand->GetColumnString(1); + row.exportable = selectCommand->GetColumnInteger(2); + row.dataType = DataType(selectCommand->GetColumnInteger(3)); + row.algorithmType = static_cast(selectCommand->GetColumnInteger(4)); + row.encryptionScheme = selectCommand->GetColumnInteger(5); + row.iv = selectCommand->GetColumnBlob(6); + row.dataSize = selectCommand->GetColumnInteger(7); + row.data = selectCommand->GetColumnBlob(8); + row.tag = selectCommand->GetColumnBlob(9); + row.backendId = static_cast(selectCommand->GetColumnInteger(11)); + return row; + } + + PermissionMaskOptional Crypto::getPermissionRow( + const Name &name, + const Label &ownerLabel, + const Label &accessorLabel) const + { + Try { + PermissionTable permissionTable(this->m_connection); + return permissionTable.getPermissionRow(name, ownerLabel, accessorLabel); } Catch (SqlConnection::Exception::InvalidColumn) { LogError("Select statement invalid column error"); } Catch (SqlConnection::Exception::SyntaxError) { @@ -304,29 +443,42 @@ using namespace DB; } Catch (SqlConnection::Exception::InternalError) { LogError("Couldn't execute select statement"); } - ThrowMsg(DBCrypto::Exception::InternalError, - "Couldn't get row for type " << static_cast(type) << - " alias " << alias << " and label " << label); + return PermissionMaskOptional(); } - DBCrypto::DBRowOptional DBCrypto::getKeyDBRow( - const Alias &alias, - const std::string &label) + Crypto::RowOptional Crypto::getRow( + const Name &name, + const Label &ownerLabel, + DataType type) { - Try{ - Transaction transaction(this); + return getRow(name, ownerLabel, type, type); + } + + Crypto::RowOptional Crypto::getRow( + const Name &name, + const Label &ownerLabel, + DataType typeRangeStart, + DataType typeRangeStop) + { + Try { SqlConnection::DataCommandUniquePtr selectCommand = - m_connection->PrepareDataCommand(select_key_alias_cmd); - selectCommand->BindString(1, alias.c_str()); - selectCommand->BindInteger(2, static_cast(DBDataType::DB_KEY_FIRST)); - selectCommand->BindInteger(3, static_cast(DBDataType::DB_KEY_LAST)); - selectCommand->BindString(4, label.c_str()); + m_connection->PrepareDataCommand(DB_CMD_OBJECT_SELECT_BY_NAME_AND_LABEL); + selectCommand->BindInteger(1, typeRangeStart); + selectCommand->BindInteger(2, typeRangeStop); - if(selectCommand->Step()) { - transaction.commit(); - return DBRowOptional(getRow(selectCommand)); + // name table reference + selectCommand->BindString (101, name.c_str()); + selectCommand->BindString (102, ownerLabel.c_str()); + + if(selectCommand->Step()) + { + // extract data + Row current_row = getRow(selectCommand); + + // all okay, proceed + return RowOptional(current_row); } else { - return DBRowOptional(); + return RowOptional(); } } Catch (SqlConnection::Exception::InvalidColumn) { LogError("Select statement invalid column error"); @@ -335,27 +487,43 @@ using namespace DB; } Catch (SqlConnection::Exception::InternalError) { LogError("Couldn't execute select statement"); } - ThrowMsg(DBCrypto::Exception::InternalError, - "Couldn't get Key for alias " << alias - << " and label " << label); + ThrowMsg(Crypto::Exception::InternalError, + "Couldn't get row of type <" << + static_cast(typeRangeStart) << "," << + static_cast(typeRangeStop) << ">" << + " name " << name << " with owner label " << ownerLabel); } - void DBCrypto::getSingleType( - DBDataType type, - const std::string& label, - AliasVector& aliases) + void Crypto::getRows( + const Name &name, + const Label &ownerLabel, + DataType type, + RowVector &output) { - Try{ - SqlConnection::DataCommandUniquePtr selectCommand = - m_connection->PrepareDataCommand(select_type_cmd); - selectCommand->BindInteger(1, static_cast(type)); - selectCommand->BindInteger(2, static_cast(type)); - selectCommand->BindString(3, label.c_str()); + getRows(name, ownerLabel, type, type, output); + } - while(selectCommand->Step()) { - Alias alias; - alias = selectCommand->GetColumnString(0); - aliases.push_back(alias); + void Crypto::getRows( + const Name &name, + const Label &ownerLabel, + DataType typeRangeStart, + DataType typeRangeStop, + RowVector &output) + { + Try { + SqlConnection::DataCommandUniquePtr selectCommand = + m_connection->PrepareDataCommand(DB_CMD_OBJECT_SELECT_BY_NAME_AND_LABEL); + selectCommand->BindInteger(1, typeRangeStart); + selectCommand->BindInteger(2, typeRangeStop); + + // name table reference + selectCommand->BindString (101, name.c_str()); + selectCommand->BindString (102, ownerLabel.c_str()); + + while(selectCommand->Step()) + { + // extract data + output.push_back(getRow(selectCommand)); } return; } Catch (SqlConnection::Exception::InvalidColumn) { @@ -365,38 +533,41 @@ using namespace DB; } Catch (SqlConnection::Exception::InternalError) { LogError("Couldn't execute select statement"); } - ThrowMsg(DBCrypto::Exception::InternalError, - "Couldn't get type " << static_cast(type) - << " for label " << label); + ThrowMsg(Crypto::Exception::InternalError, + "Couldn't get row of type <" << + static_cast(typeRangeStart) << "," << + static_cast(typeRangeStop) << ">" << + " name " << name << " with owner label " << ownerLabel); } - void DBCrypto::getAliases( - DBDataType type, - const std::string& label, - AliasVector& aliases) + void Crypto::listNames( + const Label &smackLabel, + LabelNameVector& labelNameVector, + DataType type) { - getSingleType(type, label, aliases); + listNames(smackLabel, labelNameVector, type, type); } - - void DBCrypto::getKeyAliases( - const std::string &label, - AliasVector &aliases) + void Crypto::listNames( + const Label &smackLabel, + LabelNameVector& labelNameVector, + DataType typeRangeStart, + DataType typeRangeStop) { Try{ Transaction transaction(this); SqlConnection::DataCommandUniquePtr selectCommand = - m_connection->PrepareDataCommand(select_key_type_cmd); - selectCommand->BindInteger(1, static_cast(DBDataType::DB_KEY_FIRST)); - selectCommand->BindInteger(2, static_cast(DBDataType::DB_KEY_LAST)); - selectCommand->BindString(3, label.c_str()); + m_connection->PrepareDataCommand(DB_CMD_NAME_SELECT_BY_TYPE_AND_PERMISSION); + selectCommand->BindInteger(1, static_cast(typeRangeStart)); + selectCommand->BindInteger(2, static_cast(typeRangeStop)); + selectCommand->BindString(104, smackLabel.c_str()); + selectCommand->BindInteger(4, static_cast(Permission::READ | Permission::REMOVE)); while(selectCommand->Step()) { - Alias alias; - alias = selectCommand->GetColumnString(0); - aliases.push_back(alias); + Label ownerLabel = selectCommand->GetColumnString(0); + Name name = selectCommand->GetColumnString(1); + labelNameVector.push_back(std::make_pair(ownerLabel, name)); } - transaction.commit(); return; } Catch (SqlConnection::Exception::InvalidColumn) { LogError("Select statement invalid column error"); @@ -405,70 +576,46 @@ 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(Crypto::Exception::InternalError, + "Couldn't list names of type <" << + static_cast(typeRangeStart) << "," << + static_cast(typeRangeStop) << ">" << + " accessible to client label " << smackLabel); } - void DBCrypto::deleteDBRow( - const Alias &alias, - const std::string &label) - { - Try { - Transaction transaction(this); - 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; - } 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); - } - void DBCrypto::saveKey( - const std::string& label, + + void Crypto::saveKey( + const Label& label, const RawBuffer &key) { Try { - Transaction transaction(this); SqlConnection::DataCommandUniquePtr insertCommand = - m_connection->PrepareDataCommand(insert_key_cmd); + m_connection->PrepareDataCommand(DB_CMD_KEY_INSERT); insertCommand->BindString(1, label.c_str()); insertCommand->BindBlob(2, key); insertCommand->Step(); - transaction.commit(); return; } Catch (SqlConnection::Exception::SyntaxError) { LogError("Couldn't prepare insert key statement"); } Catch (SqlConnection::Exception::InternalError) { LogError("Couldn't execute insert statement"); } - ThrowMsg(DBCrypto::Exception::InternalError, + ThrowMsg(Crypto::Exception::InternalError, "Couldn't save key for label " << label); } - DBCrypto::RawBufferOptional DBCrypto::getKey( - const std::string& label) + Crypto::RawBufferOptional Crypto::getKey(const Label& label) { Try { - Transaction transaction(this); SqlConnection::DataCommandUniquePtr selectCommand = - m_connection->PrepareDataCommand(select_key_cmd); + m_connection->PrepareDataCommand(DB_CMD_KEY_SELECT); selectCommand->BindString(1, label.c_str()); if (selectCommand->Step()) { - transaction.commit(); return RawBufferOptional( selectCommand->GetColumnBlob(0)); } else { - transaction.commit(); return RawBufferOptional(); } @@ -479,17 +626,22 @@ using namespace DB; } Catch (SqlConnection::Exception::InternalError) { LogError("Couldn't execute insert statement"); } - ThrowMsg(DBCrypto::Exception::InternalError, + ThrowMsg(Crypto::Exception::InternalError, "Couldn't get key for label " << label); } - void DBCrypto::deleteKey(const std::string& label) { + void Crypto::deleteKey(const Label& label) { Try { Transaction transaction(this); + SqlConnection::DataCommandUniquePtr deleteCommand = - m_connection->PrepareDataCommand(delete_key_cmd); + m_connection->PrepareDataCommand(DB_CMD_KEY_DELETE); deleteCommand->BindString(1, label.c_str()); deleteCommand->Step(); + + NameTable nameTable(this->m_connection); + nameTable.deleteAllRows(label); + transaction.commit(); return; } Catch (SqlConnection::Exception::SyntaxError) { @@ -497,10 +649,253 @@ using namespace DB; } Catch (SqlConnection::Exception::InternalError) { LogError("Couldn't execute insert statement"); } - ThrowMsg(DBCrypto::Exception::InternalError, + ThrowMsg(Crypto::Exception::InternalError, "Couldn't delete key for label " << label); } -} // CKM + void Crypto::setPermission( + const Name &name, + const Label& ownerLabel, + const Label& accessorLabel, + const PermissionMask permissionMask) + { + Try { + PermissionTable permissionTable(this->m_connection); + permissionTable.setPermission(name, ownerLabel, accessorLabel, permissionMask); + return; + } Catch (SqlConnection::Exception::SyntaxError) { + LogError("Couldn't prepare set statement"); + } Catch (SqlConnection::Exception::InternalError) { + LogError("Couldn't execute set statement"); + } + ThrowMsg(Crypto::Exception::InternalError, + "Couldn't set permissions for name " << name ); + } + + + void Crypto::SchemaInfo::setVersionInfo() { + SqlConnection::DataCommandUniquePtr insertContextCommand = + m_db->m_connection->PrepareDataCommand(DB_CMD_SCHEMA_SET); + insertContextCommand->BindString(101, DB_SCHEMA_VERSION_FIELD); + insertContextCommand->BindString(103, std::to_string(DB_VERSION_CURRENT).c_str()); + insertContextCommand->Step(); + } + + bool Crypto::SchemaInfo::getVersionInfo(int & version) const + { + // Try..Catch mandatory here - we don't need to escalate the error + // if it happens - we just won't return the version, allowing CKM to work + Try { + SqlConnection::DataCommandUniquePtr selectCommand = + m_db->m_connection->PrepareDataCommand(DB_CMD_SCHEMA_GET); + selectCommand->BindString(101, DB_SCHEMA_VERSION_FIELD); + + if(selectCommand->Step()) { + version = static_cast(atoi(selectCommand->GetColumnString(1).c_str())); + return true; + } + } 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 false; + } + + void Crypto::PermissionTable::setPermission( + const Name &name, + const Label& ownerLabel, + const Label& accessorLabel, + const PermissionMask permissionMask) + { + if(permissionMask == Permission::NONE) + { + // clear permissions + SqlConnection::DataCommandUniquePtr deletePermissionCommand = + m_connection->PrepareDataCommand(DB_CMD_PERMISSION_DELETE); + deletePermissionCommand->BindString(104, accessorLabel.c_str()); + deletePermissionCommand->BindString(101, name.c_str()); + deletePermissionCommand->BindString(102, ownerLabel.c_str()); + deletePermissionCommand->Step(); + } + else + { + // add new permissions + SqlConnection::DataCommandUniquePtr setPermissionCommand = + m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SET); + setPermissionCommand->BindString(104, accessorLabel.c_str()); + setPermissionCommand->BindInteger(105, static_cast(permissionMask)); + setPermissionCommand->BindString(101, name.c_str()); + setPermissionCommand->BindString(102, ownerLabel.c_str()); + setPermissionCommand->Step(); + } + } + + PermissionMaskOptional Crypto::PermissionTable::getPermissionRow( + const Name &name, + const Label &ownerLabel, + const Label &accessorLabel) const + { + SqlConnection::DataCommandUniquePtr selectCommand = + m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SELECT); + selectCommand->BindString(104, accessorLabel.c_str()); + + // name table reference + selectCommand->BindString(101, name.c_str()); + selectCommand->BindString(102, ownerLabel.c_str()); + + if(selectCommand->Step()) + { + // there is entry for the pair + return PermissionMaskOptional(PermissionMask(selectCommand->GetColumnInteger(0))); + } + return PermissionMaskOptional(); + } + + void Crypto::NameTable::addRow( + const Name &name, + const Label &ownerLabel) + { + // insert NAMES item + SqlConnection::DataCommandUniquePtr insertNameCommand = + m_connection->PrepareDataCommand(DB_CMD_NAME_INSERT); + insertNameCommand->BindString (101, name.c_str()); + insertNameCommand->BindString (102, ownerLabel.c_str()); + insertNameCommand->Step(); + } + + void Crypto::NameTable::deleteRow( + const Name &name, + const Label &ownerLabel) + { + SqlConnection::DataCommandUniquePtr deleteCommand = + m_connection->PrepareDataCommand(DB_CMD_NAME_DELETE); + deleteCommand->BindString(101, name.c_str()); + deleteCommand->BindString(102, ownerLabel.c_str()); + + // Step() result code does not provide information whether + // anything was removed. + deleteCommand->Step(); + } + + void Crypto::NameTable::deleteAllRows(const Label &ownerLabel) + { + SqlConnection::DataCommandUniquePtr deleteData = + m_connection->PrepareDataCommand(DB_CMD_NAME_DELETE_BY_LABEL); + deleteData->BindString(102, ownerLabel.c_str()); + + // Step() result code does not provide information whether + // anything was removed. + deleteData->Step(); + } + + bool Crypto::NameTable::isPresent(const Name &name, const Label &ownerLabel) const + { + SqlConnection::DataCommandUniquePtr checkCmd = + m_connection->PrepareDataCommand(DB_CMD_NAME_COUNT_ROWS); + checkCmd->BindString(101, name.c_str()); + checkCmd->BindString(102, ownerLabel.c_str()); + if(checkCmd->Step()) { + int element_count = checkCmd->GetColumnInteger(0); + LogDebug("Item name: " << name << " ownerLabel: " << ownerLabel << + " hit count: " << element_count); + if(element_count > 0) + return true; + } + return false; + } + + void Crypto::ObjectTable::addRow(const Row &row) + { + SqlConnection::DataCommandUniquePtr insertObjectCommand = + m_connection->PrepareDataCommand(DB_CMD_OBJECT_INSERT); + insertObjectCommand->BindInteger(1, row.exportable); + insertObjectCommand->BindInteger(2, static_cast(row.dataType)); + insertObjectCommand->BindInteger(3, static_cast(row.algorithmType)); + insertObjectCommand->BindInteger(4, row.encryptionScheme); + insertObjectCommand->BindBlob (5, row.iv); + insertObjectCommand->BindInteger(6, row.dataSize); + insertObjectCommand->BindBlob (7, row.data); + insertObjectCommand->BindBlob (8, row.tag); + insertObjectCommand->BindInteger(9, static_cast(row.backendId)); + + // name table reference + insertObjectCommand->BindString (101, row.name.c_str()); + insertObjectCommand->BindString (102, row.ownerLabel.c_str()); + + insertObjectCommand->Step(); + } + + std::string Crypto::getSchema() { + SqlConnection::DataCommandUniquePtr schema = + m_connection->PrepareDataCommand("SELECT sql FROM " + "(SELECT * FROM sqlcipher_master UNION ALL " + "SELECT * FROM sqlcipher_temp_master) " + "WHERE type!='meta' " + "ORDER BY tbl_name, type DESC, name;"); + + std::stringstream ss; + while(schema->Step()) { + ss << schema->GetColumnString(0) << std::endl; + } + return ss.str(); + } + + std::string Crypto::getContent() { + SqlConnection::DataCommandUniquePtr tableSelect = + m_connection->PrepareDataCommand( + "SELECT name FROM sqlcipher_master " + "WHERE type IN ('table','view') AND name NOT LIKE 'sqlcipher_%' " + "UNION ALL " + "SELECT name FROM sqlcipher_temp_master " + "WHERE type IN ('table','view') " + "ORDER BY 1; "); + + std::vector tables; + while(tableSelect->Step()) { + tables.push_back(tableSelect->GetColumnString(0)); + } + + std::stringstream ss; + + for (auto &e : tables) { + ss << "Table " << e << std::endl; + std::string query = "select * from " + e + ";"; + SqlConnection::DataCommandUniquePtr result = + m_connection->PrepareDataCommand(query.c_str()); + while(result->Step()) { + int maxColumn = result->GetColumnCount(); + for (int i = 0; i < maxColumn; ++i) { + switch(result->GetColumnType(i)) { + case 1: // int64 + ss << result->GetColumnInteger(i) << " | "; + break; + case 2: // float + ss << result->GetColumnFloat(i) << " | "; + break; + case 3: // string + ss << result->GetColumnString(i) << " | "; + break; + case 4: // Blob + { + auto buffer = result->GetColumnBlob(i); + ss << "BLOB (Size: " << buffer.size() << ") | "; + break; + } + case 5: // NULL + ss << "NULL | "; + break; + } + } + ss << std::endl; + } + } + + return ss.str(); + } +} // namespace DB +} // namespace CKM #pragma GCC diagnostic pop