--- /dev/null
+/*
+ * Copyright (c) 2000 - 2015 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 create_schema.sql
+ * @author Maciej Karpiuk (m.karpiuk2@samsung.com)
+ * @version 3.0
+ * @brief DB script to create database schema.
+ */
+
+
+-- create the tables
+CREATE TABLE IF NOT EXISTS SCHEMA_INFO(name TEXT PRIMARY KEY NOT NULL,
+ value TEXT);
+
+CREATE TABLE IF NOT EXISTS NAME_TABLE(name TEXT NOT NULL,
+ label TEXT NOT NULL,
+ idx INTEGER PRIMARY KEY AUTOINCREMENT,
+ UNIQUE(name, label));
+
+CREATE TABLE IF NOT EXISTS OBJECT_TABLE(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,
+ tag BLOB NOT NULL,
+ idx INTEGER NOT NULL,
+ FOREIGN KEY(idx) REFERENCES NAME_TABLE(idx) ON DELETE CASCADE,
+ PRIMARY KEY(idx, dataType));
+
+CREATE TABLE IF NOT EXISTS KEY_TABLE(label TEXT PRIMARY KEY,
+ key BLOB NOT NULL);
+
+CREATE TABLE IF NOT EXISTS PERMISSION_TABLE(permissionLabel TEXT NOT NULL,
+ permissionMask INTEGER NOT NULL,
+ idx INTEGER NOT NULL,
+ FOREIGN KEY(idx) REFERENCES NAME_TABLE(idx) ON DELETE CASCADE,
+ PRIMARY KEY(permissionLabel, idx));
+
+
+-- create views
+CREATE VIEW IF NOT EXISTS [join_name_object_tables] AS
+ SELECT N.name, N.label, O.* FROM
+ NAME_TABLE AS N
+ JOIN OBJECT_TABLE AS O ON O.idx=N.idx;
+
+CREATE VIEW IF NOT EXISTS [join_name_permission_tables] AS
+ SELECT N.name, N.label, P.permissionMask, P.permissionLabel FROM NAME_TABLE AS N
+ JOIN PERMISSION_TABLE AS P ON P.idx=N.idx;
+
+CREATE VIEW IF NOT EXISTS [join_all_tables] AS
+ SELECT N.*, P.permissionLabel, P.permissionMask, O.dataType FROM NAME_TABLE AS N
+ JOIN OBJECT_TABLE AS O ON O.idx=N.idx
+ JOIN PERMISSION_TABLE AS P ON P.idx=N.idx;
+
+
+-- create indexes
+CREATE INDEX IF NOT EXISTS perm_index_idx ON PERMISSION_TABLE(idx);
+CREATE INDEX IF NOT EXISTS name_index_idx ON NAME_TABLE(idx);
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2015 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 drop_all.sql
+ * @author Maciej Karpiuk (m.karpiuk2@samsung.com)
+ * @version 1.0
+ * @brief DB script to drop all current and historical objects.
+ */
+
+
+-- drop tables
+-- SQLite does not provide DROP ALL TABLES construction.
+-- the SQLite-way is to remove the whole database file,
+-- which would require expensive changes to the service code
+-- (re-establishing the DB connection).
+DROP TABLE IF EXISTS SCHEMA_INFO;
+DROP TABLE IF EXISTS CKM_TABLE;
+DROP TABLE IF EXISTS NAME_TABLE;
+DROP TABLE IF EXISTS KEY_TABLE;
+DROP TABLE IF EXISTS OBJECT_TABLE;
+DROP TABLE IF EXISTS PERMISSION_TABLE;
+DROP TABLE IF EXISTS OLD_PERMISSION_TABLE;
+
+
+-- drop views
+DROP VIEW IF EXISTS [join_name_object_tables];
+DROP VIEW IF EXISTS [join_name_permission_tables];
+DROP VIEW IF EXISTS [join_all_tables];
+
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2015 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 migrate_1.sql
+ * @author Maciej Karpiuk (m.karpiuk2@samsung.com)
+ * @version 1.0
+ * @brief DB migration script from schema version 1 to schema version 2.
+ */
+
+
+-- isolate old data
+ALTER TABLE PERMISSION_TABLE RENAME TO OLD_PERMISSION_TABLE;
+DROP INDEX perm_index_idx;
+
+
+-- create new structure
+CREATE TABLE NAME_TABLE(name TEXT NOT NULL,
+ label TEXT NOT NULL,
+ idx INTEGER PRIMARY KEY AUTOINCREMENT,
+ UNIQUE(name, label));
+CREATE INDEX name_index_idx ON NAME_TABLE(idx);
+CREATE TABLE OBJECT_TABLE(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,
+ tag BLOB NOT NULL,
+ idx INTEGER NOT NULL,
+ FOREIGN KEY(idx) REFERENCES NAME_TABLE(idx) ON DELETE CASCADE,
+ PRIMARY KEY(idx, dataType));
+CREATE TABLE PERMISSION_TABLE(label TEXT NOT NULL,
+ accessFlags TEXT NOT NULL,
+ idx INTEGER NOT NULL,
+ FOREIGN KEY(idx) REFERENCES NAME_TABLE(idx) ON DELETE CASCADE,
+ PRIMARY KEY(label, idx));
+CREATE INDEX perm_index_idx ON PERMISSION_TABLE(idx);
+
+
+-- move data
+INSERT INTO NAME_TABLE(name, label, idx) SELECT name, label, idx FROM CKM_TABLE;
+INSERT INTO OBJECT_TABLE(exportable, dataType, algorithmType, encryptionScheme,
+ iv, dataSize, data, tag, idx)
+ SELECT exportable, dataType, algorithmType, encryptionScheme, iv,
+ dataSize, data, tag, idx FROM CKM_TABLE;
+INSERT INTO PERMISSION_TABLE(label, accessFlags, idx) SELECT label, accessFlags, idx FROM OLD_PERMISSION_TABLE;
+
+
+-- cleanup
+DROP TABLE OLD_PERMISSION_TABLE;
+DROP TABLE CKM_TABLE;
--- /dev/null
+/*
+ * Copyright (c) 2000 - 2015 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 migrate_2.sql
+ * @author Maciej Karpiuk (m.karpiuk2@samsung.com)
+ * @version 1.0
+ * @brief DB migration script from schema version 2 to schema version 3.
+ */
+
+
+-- isolate old data
+ALTER TABLE PERMISSION_TABLE RENAME TO OLD_PERMISSION_TABLE;
+DROP INDEX perm_index_idx;
+
+
+-- create new structure
+CREATE TABLE SCHEMA_INFO(name TEXT PRIMARY KEY NOT NULL,
+ value TEXT);
+CREATE TABLE PERMISSION_TABLE(permissionLabel TEXT NOT NULL,
+ permissionMask INTEGER NOT NULL,
+ idx INTEGER NOT NULL,
+ FOREIGN KEY(idx) REFERENCES NAME_TABLE(idx) ON DELETE CASCADE,
+ PRIMARY KEY(permissionLabel, idx));
+CREATE INDEX perm_index_idx ON PERMISSION_TABLE(idx);
+CREATE VIEW [join_name_object_tables] AS
+ SELECT N.name, N.label, O.* FROM NAME_TABLE AS N
+ JOIN OBJECT_TABLE AS O ON O.idx=N.idx;
+CREATE VIEW [join_name_permission_tables] AS
+ SELECT N.name, N.label, P.permissionMask, P.permissionLabel FROM NAME_TABLE AS N
+ JOIN PERMISSION_TABLE AS P ON P.idx=N.idx;
+CREATE VIEW [join_all_tables] AS
+ SELECT N.*, P.permissionLabel, P.permissionMask, O.dataType FROM NAME_TABLE AS N
+ JOIN OBJECT_TABLE AS O ON O.idx=N.idx
+ JOIN PERMISSION_TABLE AS P ON P.idx=N.idx;
+
+
+-- move data
+INSERT INTO PERMISSION_TABLE(permissionLabel, permissionMask, idx) SELECT label, 1, idx FROM OLD_PERMISSION_TABLE WHERE accessFlags='R';
+INSERT INTO PERMISSION_TABLE(permissionLabel, permissionMask, idx) SELECT label, 3, idx FROM OLD_PERMISSION_TABLE WHERE accessFlags='RD';
+INSERT INTO PERMISSION_TABLE(permissionLabel, permissionMask, idx) SELECT label, 3, idx FROM NAME_TABLE;
+
+
+-- cleanup
+DROP TABLE OLD_PERMISSION_TABLE;
cp LICENSE %{buildroot}/usr/share/license/libkey-manager-client
cp LICENSE %{buildroot}/usr/share/license/libkey-manager-control-client
mkdir -p %{buildroot}/etc/security/
+mkdir -p %{buildroot}/usr/share/ckm/scripts
+cp data/scripts/*.sql %{buildroot}/usr/share/ckm/scripts
+mkdir -p %{buildroot}/usr/share/ckm-db-test
+cp tests/testme_ver1.db %{buildroot}/usr/share/ckm-db-test/
+cp tests/testme_ver2.db %{buildroot}/usr/share/ckm-db-test/
%make_install
mkdir -p %{buildroot}%{_unitdir}/multi-user.target.wants
%{_unitdir}/sockets.target.wants/central-key-manager-api-ocsp.socket
%{_unitdir}/central-key-manager-api-ocsp.socket
%{_datadir}/license/%{name}
+%{_datadir}/ckm/scripts/*.sql
+%attr(444, root, root) %{_datadir}/ckm/scripts/*.sql
%files -n key-manager-listener
%manifest key-manager-listener.manifest
%files -n key-manager-tests
%defattr(-,root,root,-)
%{_bindir}/ckm-tests-internal
+%{_datadir}/ckm-db-test/testme_ver1.db
+%{_datadir}/ckm-db-test/testme_ver2.db
* @brief Implementation of encrypted db access layer
*/
+#include <fstream>
#include <db-crypto.h>
#include <dpl/db/sql_connection.h>
#include <dpl/log/log.h>
#pragma GCC diagnostic warning "-Wdeprecated-declarations"
namespace {
- const char *TABLE_NAME = "NAME_TABLE";
- const char *TABLE_OBJECT = "OBJECT_TABLE";
- const char *TABLE_KEY = "KEY_TABLE";
- const char *TABLE_PERMISSION = "PERMISSION_TABLE";
- const CKM::PermissionMask DEFAULT_PERMISSIONS = static_cast<CKM::PermissionMask>(CKM::Permission::READ | CKM::Permission::REMOVE);
-
- const char *DB_CMD_NAME_CREATE =
- "CREATE TABLE IF NOT EXISTS NAME_TABLE("
- " name TEXT NOT NULL,"
- " label TEXT NOT NULL,"
- " idx INTEGER PRIMARY KEY AUTOINCREMENT,"
- " UNIQUE(name, label)"
- "); CREATE INDEX IF NOT EXISTS name_index_idx ON NAME_TABLE(idx);";
+ const CKM::PermissionMask DEFAULT_PERMISSIONS =
+ static_cast<CKM::PermissionMask>(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 = 3
+ };
+
+ 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 NAME_TABLE("
const char *DB_CMD_NAME_DELETE_BY_LABEL =
"DELETE FROM NAME_TABLE WHERE label=?102;";
- const char *DB_CMD_OBJECT_CREATE =
- "CREATE TABLE IF NOT EXISTS OBJECT_TABLE("
- " 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,"
- " tag BLOB NOT NULL,"
- " idx INTEGER NOT NULL,"
- " FOREIGN KEY(idx) REFERENCES NAME_TABLE(idx) ON DELETE CASCADE,"
- " PRIMARY KEY(idx, dataType)"
- ");"; // TODO: index and performance tests
const char *DB_CMD_OBJECT_INSERT =
"INSERT INTO OBJECT_TABLE("
" WHERE (dataType BETWEEN ?001 AND ?002) "
" AND name=?101 and label=?102;";
- const char *DB_CMD_KEY_CREATE =
- "CREATE TABLE IF NOT EXISTS KEY_TABLE("
- " label TEXT PRIMARY KEY,"
- " key BLOB NOT NULL"
- ");";
const char *DB_CMD_KEY_INSERT =
"INSERT INTO KEY_TABLE(label, key) VALUES (?, ?);";
"DELETE FROM KEY_TABLE WHERE label=?";
- const char *DB_CMD_PERMISSION_CREATE =
- "CREATE TABLE IF NOT EXISTS PERMISSION_TABLE("
- " permissionLabel TEXT NOT NULL,"
- " permissionMask INTEGER NOT NULL,"
- " idx INTEGER NOT NULL,"
- " FOREIGN KEY(idx) REFERENCES NAME_TABLE(idx) ON DELETE CASCADE,"
- " PRIMARY KEY(permissionLabel, idx)"
- "); CREATE INDEX IF NOT EXISTS perm_index_idx ON PERMISSION_TABLE(idx);"; // based on ANALYZE and performance test result
-
const char *DB_CMD_PERMISSION_SET = // SQLite does not support updating views
"REPLACE INTO PERMISSION_TABLE(permissionLabel, permissionMask, idx) "
- " VALUES (?001, ?002, (SELECT idx FROM NAME_TABLE WHERE name=?101 and label=?102));";
+ " VALUES (?104, ?105, (SELECT idx FROM NAME_TABLE WHERE name=?101 and label=?102));";
const char *DB_CMD_PERMISSION_SELECT =
"SELECT permissionMask FROM [join_name_permission_tables] "
- " WHERE permissionLabel=?001 "
+ " WHERE permissionLabel=?104 "
" AND name=?101 and label=?102;";
const char *DB_CMD_PERMISSION_DELETE = // SQLite does not support updating views
- "DELETE FROM PERMISSION_TABLE WHERE permissionLabel=?001 AND "
+ "DELETE FROM PERMISSION_TABLE WHERE permissionLabel=?104 AND "
" idx=(SELECT idx FROM NAME_TABLE WHERE name=?101 and label=?102);";
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=?003 AND permissionMask&?004!=0 GROUP BY idx;";
-
- const char *DB_CMD_CREATE_JOIN_NAME_OBJECT_VIEW =
- "CREATE VIEW IF NOT EXISTS [join_name_object_tables] AS"
- " SELECT N.name, N.label, O.* FROM "
- " NAME_TABLE AS N "
- " JOIN OBJECT_TABLE AS O ON O.idx=N.idx;";
-
- const char *DB_CMD_CREATE_JOIN_NAME_PERMISSION_VIEW =
- "CREATE VIEW IF NOT EXISTS [join_name_permission_tables] AS"
- " SELECT N.name, N.label, P.permissionMask, P.permissionLabel FROM "
- " NAME_TABLE AS N "
- " JOIN PERMISSION_TABLE AS P ON P.idx=N.idx;";
-
- const char *DB_CMD_CREATE_ALL_JOIN_VIEW =
- "CREATE VIEW IF NOT EXISTS [join_all_tables] AS"
- " SELECT N.*, P.permissionLabel, P.permissionMask, O.dataType FROM "
- " NAME_TABLE AS N "
- " JOIN OBJECT_TABLE AS O ON O.idx=N.idx "
- " JOIN PERMISSION_TABLE AS P ON P.idx=N.idx;";
+ " AND permissionLabel=?104 AND permissionMask&?004!=0 GROUP BY idx;";
}
namespace CKM {
Try {
m_connection = new SqlConnection(path, SqlConnection::Flag::Option::CRW);
m_connection->SetKey(rawPass);
- m_connection->ExecCommand("VACUUM;");
initDatabase();
+ m_connection->ExecCommand("VACUUM;");
} Catch(SqlConnection::Exception::ConnectionBroken) {
LogError("Couldn't connect to database: " << path);
ReThrow(DBCrypto::Exception::InternalError);
LogError("Couldn't initiate the database");
ReThrow(DBCrypto::Exception::InternalError);
} Catch(SqlConnection::Exception::InternalError) {
- LogError("Couldn't create the database");
+ LogError("Couldn't intialize the database");
ReThrow(DBCrypto::Exception::InternalError);
}
}
}
}
- void DBCrypto::initDatabase() {
+ bool DBCrypto::getDBVersion(int & schemaVersion)
+ {
+ SchemaInfo SchemaInfo(this);
+ if(SchemaInfo.getVersionInfo(schemaVersion)) {
+ LogDebug("Current DB version: " << schemaVersion);
+ return true;
+ }
+ 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;
+ }
+
+ // 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::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);
+ for(int vi=schemaVersion; vi<DB_VERSION_CURRENT; vi++)
+ {
+ ScriptOptional script = getMigrationScript(vi);
+ if(!script)
+ {
+ LogError("Error, script to migrate database from version: " << vi <<
+ " to version: " << vi+1 << " not available, resetting the DB");
+ resetDB();
+ break;
+ }
+
+ LogInfo("migrating from version " << vi << " to version " << vi+1);
+ m_connection->ExecCommand((*script).c_str());
+ }
+ // update DB version info
+ SchemaInfo SchemaInfo(this);
+ SchemaInfo.setVersionInfo();
+ transaction.commit();
+ }
+ }
+
+ DBCrypto::ScriptOptional DBCrypto::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();
+ }
+
+ std::istreambuf_iterator<char> begin(is),end;
+ return ScriptOptional(std::string(begin, end));
+ }
+ DBCrypto::ScriptOptional DBCrypto::getMigrationScript(int db_version) const
+ {
+ std::string scriptPath = std::string(SCRIPT_MIGRATE) + std::to_string(db_version);
+ return getScript(scriptPath);
+ }
+
+ void DBCrypto::createDBSchema() {
Transaction transaction(this);
- createTable(DB_CMD_NAME_CREATE, TABLE_NAME);
- createTable(DB_CMD_OBJECT_CREATE, TABLE_OBJECT);
- createTable(DB_CMD_KEY_CREATE, TABLE_KEY);
- createTable(DB_CMD_PERMISSION_CREATE, TABLE_PERMISSION);
- createView(DB_CMD_CREATE_ALL_JOIN_VIEW);
- createView(DB_CMD_CREATE_JOIN_NAME_OBJECT_VIEW);
- createView(DB_CMD_CREATE_JOIN_NAME_PERMISSION_VIEW);
+
+ 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 DBCrypto::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();
}
m_connection->PrepareDataCommand(DB_CMD_NAME_SELECT_BY_TYPE_AND_PERMISSION);
selectCommand->BindInteger(1, static_cast<int>(typeRangeStart));
selectCommand->BindInteger(2, static_cast<int>(typeRangeStop));
- selectCommand->BindString(3, smackLabel.c_str());
+ selectCommand->BindString(104, smackLabel.c_str());
selectCommand->BindInteger(4, static_cast<int>(Permission::READ | Permission::REMOVE));
while(selectCommand->Step()) {
"Couldn't set permissions for name " << name );
}
+
+ void DBCrypto::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 DBCrypto::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<int>(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 DBCrypto::PermissionTable::setPermission(
const Name &name,
const Label& ownerLabel,
// clear permissions
SqlConnection::DataCommandUniquePtr deletePermissionCommand =
m_connection->PrepareDataCommand(DB_CMD_PERMISSION_DELETE);
- deletePermissionCommand->BindString(1, accessorLabel.c_str());
+ deletePermissionCommand->BindString(104, accessorLabel.c_str());
deletePermissionCommand->BindString(101, name.c_str());
deletePermissionCommand->BindString(102, ownerLabel.c_str());
deletePermissionCommand->Step();
// add new permissions
SqlConnection::DataCommandUniquePtr setPermissionCommand =
m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SET);
- setPermissionCommand->BindString(1, accessorLabel.c_str());
- setPermissionCommand->BindInteger(2, static_cast<int>(permissionMask));
+ setPermissionCommand->BindString(104, accessorLabel.c_str());
+ setPermissionCommand->BindInteger(105, static_cast<int>(permissionMask));
setPermissionCommand->BindString(101, name.c_str());
setPermissionCommand->BindString(102, ownerLabel.c_str());
setPermissionCommand->Step();
{
SqlConnection::DataCommandUniquePtr selectCommand =
m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SELECT);
- selectCommand->BindString(1, accessorLabel.c_str());
+ selectCommand->BindString(104, accessorLabel.c_str());
// name table reference
selectCommand->BindString(101, name.c_str());
m_connection(NULL),
m_inUserTransaction(false)
{};
- //user name instead of path?
+ // user name instead of path?
DBCrypto(const std::string &path, const RawBuffer &rawPass);
DBCrypto(const DBCrypto &other) = delete;
DBCrypto(DBCrypto &&other);
DB::SqlConnection* m_connection;
bool m_inUserTransaction;
+ void resetDB();
void initDatabase();
+ void createDBSchema();
+ /**
+ * return current database version
+ *
+ * @param[out] schemaVersion if success, will contain DB schema version code
+ *
+ * @return false on DB empty or corrupted, true if information read
+ */
+ bool getDBVersion(int & schemaVersion);
+ typedef boost::optional<std::string> ScriptOptional;
+ ScriptOptional getScript(const std::string &scriptName) const;
+ ScriptOptional getMigrationScript(int db_version) const;
+
DBRow getRow(
const DB::SqlConnection::DataCommandUniquePtr &selectCommand) const;
void createView(
const char* create_cmd);
+ class SchemaInfo {
+ public:
+ explicit SchemaInfo(const DBCrypto *db) : m_db(db) {}
+
+ void setVersionInfo();
+ bool getVersionInfo(int & version) const;
+
+ private:
+ const DBCrypto *m_db;
+ };
+
+ public:
class NameTable {
public:
explicit NameTable(DB::SqlConnection* connection) : m_connection(connection) {}
#include <db-crypto.h>
#include <ckm/ckm-error.h>
#include <DBFixture.h>
+#include <fstream>
using namespace CKM;
using namespace std::chrono;
DBFixture::DBFixture()
{
+ BOOST_CHECK(unlink(m_crypto_db_fname) == 0 || errno == ENOENT);
+ init();
+}
+DBFixture::DBFixture(const char *db_fname)
+{
+ BOOST_CHECK(unlink(m_crypto_db_fname) == 0 || errno == ENOENT);
+
+ // copy file
+ std::ifstream f1(db_fname, std::fstream::binary);
+ std::ofstream f2(m_crypto_db_fname, std::fstream::trunc|std::fstream::binary);
+ f2 << f1.rdbuf();
+ f2.close();
+ f1.close();
+
+ init();
+}
+
+void DBFixture::init()
+{
high_resolution_clock::time_point srand_feed = high_resolution_clock::now();
srand(srand_feed.time_since_epoch().count());
- BOOST_CHECK(unlink(m_crypto_db_fname) == 0 || errno == ENOENT);
BOOST_REQUIRE_NO_THROW(m_db = DBCrypto(m_crypto_db_fname, defaultPass));
}
output = ss.str();
}
-void DBFixture::generate_perf_DB(unsigned int num_name, unsigned int num_label)
+void DBFixture::generate_perf_DB(unsigned int num_name, unsigned int num_elements)
{
// to speed up data creation - cache the row
DBRow rowPattern = create_default_row(DBDataType::BINARY_DATA);
for(unsigned int i=0; i<num_name; i++)
{
generate_name(i, rowPattern.name);
- generate_label(i/num_label, rowPattern.ownerLabel);
+ generate_label(i/num_elements, rowPattern.ownerLabel);
BOOST_REQUIRE_NO_THROW(m_db.saveDBRow(rowPattern));
}
{
public:
DBFixture();
+ DBFixture(const char *db_fname);
constexpr static const char* m_default_name = "name";
constexpr static const char* m_default_label = "label";
CKM::DBCrypto m_db;
private:
+ void init();
double performance_get_time_elapsed_ms();
constexpr static const char* m_crypto_db_fname = "/tmp/testme.db";
}
performance_stop(c_test_retries/num_labels);
}
+BOOST_AUTO_TEST_SUITE_END()
+
+
+BOOST_AUTO_TEST_SUITE(DBCRYPTO_MIGRATION_TEST)
+namespace
+{
+const unsigned migration_names = 16107;
+const unsigned migration_labels = 273;
+const unsigned migration_reference_label_idx = 0;
+const unsigned migration_accessed_element_idx = 7;
+
+void verifyDBisValid(DBFixture & fixture)
+{
+ /**
+ * there are (migration_labels), each having (migration_names)/(migration_labels) entries.
+ * reference label (migration_reference_label_idx) exists such that it has access to
+ * all others' label element with index (migration_accessed_element_idx).
+ *
+ * Example:
+ * - migration_label_63 has access to all items owned by migration_label_63,
+ * which gives (migration_names)/(migration_labels) entries.
+ *
+ * - migration_label_0 (0 is the reference label) has access to all items
+ * owned by migration_label_0 and all others' label element index 7,
+ * which gives (migration_names)/(migration_labels) + (migration_labels-1) entries.
+ *
+ */
+ Label reference_label;
+ fixture.generate_label(migration_reference_label_idx, reference_label);
+
+ // check number of elements accessible to the reference label
+ LabelNameVector ret_list;
+ BOOST_REQUIRE_NO_THROW(fixture.m_db.listNames(reference_label, ret_list, DBDataType::BINARY_DATA));
+ BOOST_REQUIRE((migration_names/migration_labels)/*own items*/ + (migration_labels-1)/*other labels'*/ == ret_list.size());
+ ret_list.clear();
+
+ // check number of elements accessible to the other labels
+ for(unsigned int l=0; l<migration_labels; l++)
+ {
+ // bypass the reference owner label
+ if(l == migration_reference_label_idx)
+ continue;
+
+ Label current_label;
+ fixture.generate_label(l, current_label);
+ BOOST_REQUIRE_NO_THROW(fixture.m_db.listNames(current_label, ret_list, DBDataType::BINARY_DATA));
+ BOOST_REQUIRE((migration_names/migration_labels) == ret_list.size());
+ for(auto it: ret_list)
+ BOOST_REQUIRE(it.first == current_label);
+ ret_list.clear();
+ }
+}
+struct DBVer1Migration : public DBFixture
+{
+ DBVer1Migration() : DBFixture("/usr/share/ckm-db-test/testme_ver1.db")
+ {}
+};
+
+struct DBVer2Migration : public DBFixture
+{
+ DBVer2Migration() : DBFixture("/usr/share/ckm-db-test/testme_ver2.db")
+ {}
+};
+}
+
+BOOST_AUTO_TEST_CASE(DBMigrationDBVer1)
+{
+ DBVer1Migration DBver1;
+ verifyDBisValid(DBver1);
+}
+
+BOOST_AUTO_TEST_CASE(DBMigrationDBVer2)
+{
+ DBVer2Migration DBver2;
+ verifyDBisValid(DBver2);
+}
+
+BOOST_AUTO_TEST_CASE(DBMigrationDBCurrent)
+{
+ DBFixture currentDB;
+
+ // prepare data using current DB mechanism
+ Label reference_label;
+ currentDB.generate_label(migration_reference_label_idx, reference_label);
+ {
+ currentDB.generate_perf_DB(migration_names, migration_names/migration_labels);
+
+ // only the reference label has access to the other labels element <migration_accessed_element_idx>
+ for(unsigned int l=0; l<migration_labels; l++)
+ {
+ // bypass the reference owner label
+ if(l == migration_reference_label_idx)
+ continue;
+
+ unsigned element_index = migration_accessed_element_idx + l*migration_names/migration_labels;
+
+ // add permission
+ Name accessed_name;
+ currentDB.generate_name(element_index, accessed_name);
+ Label current_label;
+ currentDB.generate_label(l, current_label);
+ currentDB.add_permission(accessed_name, current_label, reference_label);
+ }
+ }
+
+ verifyDBisValid(currentDB);
+}
BOOST_AUTO_TEST_SUITE_END()