2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * @author Zofia Abramowska (z.abramowska@samsung.com)
20 * @brief Implementation of encrypted db access layer
23 #include <db-crypto.h>
24 #include <dpl/db/sql_connection.h>
25 #include <dpl/log/log.h>
26 #include <ckm/ckm-error.h>
28 #pragma GCC diagnostic push
29 #pragma GCC diagnostic warning "-Wdeprecated-declarations"
32 const char *main_table = "CKM_TABLE";
33 const char *key_table = "KEY_TABLE";
34 const char *permission_table = "PERMISSION_TABLE";
36 // CKM_TABLE (alias TEXT, label TEXT, restricted INT, exportable INT, dataType INT,
37 // algorithmType INT, encryptionScheme INT, iv BLOB, dataSize INT, data BLOB)
39 const char *db_create_main_cmd =
40 "CREATE TABLE CKM_TABLE("
41 " alias TEXT NOT NULL,"
42 " label TEXT NOT NULL,"
43 " exportable INTEGER NOT NULL,"
44 " dataType INTEGER NOT NULL,"
45 " algorithmType INTEGER NOT NULL,"
46 " encryptionScheme INTEGER NOT NULL,"
48 " dataSize INTEGER NOT NULL,"
49 " data BLOB NOT NULL,"
55 const char *insert_main_cmd =
56 "INSERT INTO CKM_TABLE("
58 " alias, label, exportable,"
60 " dataType, algorithmType, encryptionScheme,"
62 " iv, dataSize, data, tag) "
64 " ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
66 const char *select_alias_cmd =
68 "SELECT * FROM CKM_TABLE WHERE alias=? AND dataType=?; ";
70 const char *select_check_alias_cmd =
72 "SELECT dataType FROM CKM_TABLE WHERE alias=?;";
74 const char *select_check_global_alias_cmd =
76 "SELECT label FROM CKM_TABLE WHERE alias=?;";
78 const char *select_key_alias_cmd =
80 "SELECT * FROM CKM_TABLE WHERE alias=?"
82 " AND dataType BETWEEN ? AND ?;";
84 const char *delete_alias_cmd =
86 "DELETE FROM CKM_TABLE WHERE alias=?;";
88 const char *delete_data_with_key_cmd =
90 "DELETE FROM CKM_TABLE WHERE label=?;";
92 // KEY_TABLE (label TEXT, key BLOB)
94 const char *db_create_key_cmd =
95 "CREATE TABLE KEY_TABLE("
96 " label TEXT PRIMARY KEY,"
100 const char *insert_key_cmd =
101 "INSERT INTO KEY_TABLE(label, key) VALUES (?, ?);";
102 const char *select_key_cmd =
103 "SELECT key FROM KEY_TABLE WHERE label=?;";
104 const char *delete_key_cmd =
105 "DELETE FROM KEY_TABLE WHERE label=?";
108 // PERMISSION_TABLE (label TEXT, label TEXT, access_flags TEXT)
110 const char *db_create_permission_cmd =
111 "CREATE TABLE PERMISSION_TABLE("
112 " alias TEXT NOT NULL,"
113 " label TEXT NOT NULL,"
114 " accessFlags TEXT NOT NULL,"
115 " FOREIGN KEY(alias) REFERENCES CKM_TABLE(alias) ON DELETE CASCADE,"
116 " PRIMARY KEY(alias, label)"
119 const char *set_permission_alias_cmd =
120 "REPLACE INTO PERMISSION_TABLE(alias, label, accessFlags) VALUES (?, ?, ?);";
122 const char *select_permission_cmd =
124 "SELECT accessFlags FROM PERMISSION_TABLE WHERE alias=? AND label=?;";
126 const char *delete_permission_cmd =
128 "DELETE FROM PERMISSION_TABLE WHERE alias=? AND label=?;";
131 // CKM_TABLE x PERMISSION_TABLE
133 const char *select_type_cross_cmd =
135 "SELECT c.alias FROM CKM_TABLE c WHERE c.dataType=? AND (c.label=? OR c.alias IN (SELECT p.alias FROM PERMISSION_TABLE p WHERE p.label=?));";
137 const char *select_key_type_cross_cmd =
138 "SELECT c.alias FROM CKM_TABLE c WHERE "
140 " c.dataType >= ? AND "
142 " c.dataType <= ? AND "
144 " (c.label=? OR c.alias IN (SELECT p.alias FROM PERMISSION_TABLE p WHERE p.label=?));";
149 DBCrypto::DBCrypto(const std::string& path,
150 const RawBuffer &rawPass) {
152 m_inUserTransaction = false;
154 m_connection = new SqlConnection(path, SqlConnection::Flag::Option::CRW);
155 m_connection->SetKey(rawPass);
156 m_connection->ExecCommand("VACUUM;");
158 } Catch(SqlConnection::Exception::ConnectionBroken) {
159 LogError("Couldn't connect to database: " << path);
160 ReThrow(DBCrypto::Exception::InternalError);
161 } Catch(SqlConnection::Exception::InvalidArguments) {
162 LogError("Couldn't set the key for database");
163 ReThrow(DBCrypto::Exception::InternalError);
164 } Catch(SqlConnection::Exception::SyntaxError) {
165 LogError("Couldn't initiate the database");
166 ReThrow(DBCrypto::Exception::InternalError);
167 } Catch(SqlConnection::Exception::InternalError) {
168 LogError("Couldn't create the database");
169 ReThrow(DBCrypto::Exception::InternalError);
173 DBCrypto::DBCrypto(DBCrypto &&other) :
174 m_connection(other.m_connection),
175 m_inUserTransaction(other.m_inUserTransaction){
176 other.m_connection = NULL;
177 other.m_inUserTransaction = false;
180 DBCrypto::~DBCrypto() {
184 DBCrypto& DBCrypto::operator=(DBCrypto&& other) {
189 m_connection = other.m_connection;
190 other.m_connection = NULL;
192 m_inUserTransaction = other.m_inUserTransaction;
193 other.m_inUserTransaction = false;
198 void DBCrypto::createTable(
199 const char* create_cmd,
200 const char *table_name)
203 m_connection->ExecCommand(create_cmd);
204 } Catch(SqlConnection::Exception::SyntaxError) {
205 LogError("Couldn't create table : " << table_name << "!");
207 } Catch(SqlConnection::Exception::InternalError) {
208 LogError("Sqlite got into infinite busy state");
213 void DBCrypto::initDatabase() {
214 Transaction transaction(this);
215 if(!m_connection->CheckTableExist(main_table)) {
216 createTable(db_create_main_cmd, main_table);
218 if(!m_connection->CheckTableExist(key_table)) {
219 createTable(db_create_key_cmd, key_table);
221 if(!m_connection->CheckTableExist(permission_table)) {
222 createTable(db_create_permission_cmd, permission_table);
224 transaction.commit();
227 std::string DBCrypto::getLabelForAlias(const std::string& alias) const {
228 SqlConnection::DataCommandUniquePtr checkCmd =
229 m_connection->PrepareDataCommand(select_check_global_alias_cmd);
230 checkCmd->BindString(1, alias.c_str());
231 if(checkCmd->Step()) {
232 return checkCmd->GetColumnString(0);
234 return std::string();
236 bool DBCrypto::checkGlobalAliasExist(const std::string& alias) const {
237 std::string label = this->getLabelForAlias(alias);
238 if(label.empty() == false) {
239 LogDebug("Global alias '" << alias << "' exists already for label " << label);
245 bool DBCrypto::checkAliasExist(const std::string& alias) const {
246 SqlConnection::DataCommandUniquePtr checkCmd =
247 m_connection->PrepareDataCommand(select_check_alias_cmd);
248 checkCmd->BindString(1, alias.c_str());
249 if(checkCmd->Step()) {
250 LogDebug("Private alias '" << alias << "' exists already for type "
251 << checkCmd->GetColumnInteger(0));
257 void DBCrypto::saveDBRow(const DBRow &row){
260 //Sqlite does not support partial index in our version,
261 //so we do it by hand
262 Transaction transaction(this);
263 if(checkAliasExist(row.alias)) {
264 ThrowMsg(DBCrypto::Exception::AliasExists,
265 "Alias exists for alias: " << row.alias);
268 SqlConnection::DataCommandUniquePtr insertCommand =
269 m_connection->PrepareDataCommand(insert_main_cmd);
270 insertCommand->BindString(1, row.alias.c_str());
271 insertCommand->BindString(2, row.smackLabel.c_str());
272 insertCommand->BindInteger(3, row.exportable);
273 insertCommand->BindInteger(4, static_cast<int>(row.dataType));
274 insertCommand->BindInteger(5, static_cast<int>(row.algorithmType));
275 insertCommand->BindInteger(6, row.encryptionScheme);
276 insertCommand->BindBlob(7, row.iv);
277 insertCommand->BindInteger(8, row.dataSize);
278 insertCommand->BindBlob(9, row.data);
279 insertCommand->BindBlob(10, row.tag);
281 insertCommand->Step();
282 transaction.commit();
285 } Catch(SqlConnection::Exception::SyntaxError) {
286 LogError("Couldn't prepare insert statement");
287 } Catch(SqlConnection::Exception::InternalError) {
288 LogError("Couldn't execute insert statement");
290 ThrowMsg(DBCrypto::Exception::InternalError,
291 "Couldn't save DBRow");
294 DBRow DBCrypto::getRow(const SqlConnection::DataCommandUniquePtr &selectCommand) {
296 row.alias = selectCommand->GetColumnString(0);
297 row.smackLabel = selectCommand->GetColumnString(1);
298 row.exportable = selectCommand->GetColumnInteger(2);
299 row.dataType = static_cast<DBDataType>(selectCommand->GetColumnInteger(3));
300 row.algorithmType = static_cast<DBCMAlgType>(selectCommand->GetColumnInteger(4));
301 row.encryptionScheme = selectCommand->GetColumnInteger(5);
302 row.iv = selectCommand->GetColumnBlob(6);
303 row.dataSize = selectCommand->GetColumnInteger(7);
304 row.data = selectCommand->GetColumnBlob(8);
305 row.tag = selectCommand->GetColumnBlob(9);
309 std::string DBCrypto::getPermissionsForAliasAndLabel(const Alias &alias, const std::string &label) const
312 SqlConnection::DataCommandUniquePtr selectCommand =
313 m_connection->PrepareDataCommand(select_permission_cmd);
314 selectCommand->BindString(1, alias.c_str());
315 selectCommand->BindString(2, label.c_str());
317 if(selectCommand->Step())
318 return selectCommand->GetColumnString(0);
320 return std::string();
321 } Catch (SqlConnection::Exception::InvalidColumn) {
322 LogError("Select statement invalid column error");
323 } Catch (SqlConnection::Exception::SyntaxError) {
324 LogError("Couldn't prepare select statement");
325 } Catch (SqlConnection::Exception::InternalError) {
326 LogError("Couldn't execute select statement");
328 return std::string();
332 bool DBCrypto::rowAccessControlCheck(const Alias &alias,
333 const std::string &owner_label,
334 const std::string &clnt_label,
335 DBCrypto::DBOperationType access_type) const
337 // owner of the entry have all the permissions by default
338 // check if requesting client is the entry owner - if so, exit (permission granted)
339 if(owner_label == clnt_label)
342 // perform permissions DB query
343 std::string permission_string = this->getPermissionsForAliasAndLabel(alias, clnt_label);
345 // check if requested operation is in the permission string
346 LogDebug("pair <" << alias << "," << clnt_label << "> permission rights: \"" << permission_string << "\"");
347 if(permission_string.find(access_type) != std::string::npos)
352 bool DBCrypto::rowAccessControlCheck(const DBRow & input_row,
353 const std::string &clnt_label,
354 DBCrypto::DBOperationType access_type) const
356 return this->rowAccessControlCheck(input_row.alias, input_row.smackLabel, clnt_label, access_type);
360 DBCrypto::DBRowOptional DBCrypto::getDBRow(
362 const std::string &clnt_label,
366 Transaction transaction(this);
367 SqlConnection::DataCommandUniquePtr selectCommand =
368 m_connection->PrepareDataCommand(select_alias_cmd);
369 selectCommand->BindString(1, alias.c_str());
370 selectCommand->BindInteger(2, static_cast<int>(type));
372 if(selectCommand->Step())
375 DBRow current_row = getRow(selectCommand);
377 // check access rights here
378 if( ! this->rowAccessControlCheck(current_row, clnt_label, DBCrypto::DB_OPERATION_READ) )
379 ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested operation");
381 // finalize DB operations
382 transaction.commit();
385 return DBRowOptional(current_row);
387 return DBRowOptional();
389 } Catch (SqlConnection::Exception::InvalidColumn) {
390 LogError("Select statement invalid column error");
391 } Catch (SqlConnection::Exception::SyntaxError) {
392 LogError("Couldn't prepare select statement");
393 } Catch (SqlConnection::Exception::InternalError) {
394 LogError("Couldn't execute select statement");
396 ThrowMsg(DBCrypto::Exception::InternalError,
397 "Couldn't get row for type " << static_cast<int>(type) <<
398 " alias " << alias << " using client label " << clnt_label);
401 DBCrypto::DBRowOptional DBCrypto::getKeyDBRow(
403 const std::string &clnt_label)
406 Transaction transaction(this);
407 SqlConnection::DataCommandUniquePtr selectCommand =
408 m_connection->PrepareDataCommand(select_key_alias_cmd);
409 selectCommand->BindString(1, alias.c_str());
410 selectCommand->BindInteger(2, static_cast<int>(DBDataType::DB_KEY_FIRST));
411 selectCommand->BindInteger(3, static_cast<int>(DBDataType::DB_KEY_LAST));
413 if(selectCommand->Step())
416 DBRow current_row = getRow(selectCommand);
418 // check access rights here
419 if( ! this->rowAccessControlCheck(current_row, clnt_label, DBCrypto::DB_OPERATION_READ) )
420 ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested operation");
422 // finalize DB operations
423 transaction.commit();
426 return DBRowOptional(current_row);
428 return DBRowOptional();
430 } Catch (SqlConnection::Exception::InvalidColumn) {
431 LogError("Select statement invalid column error");
432 } Catch (SqlConnection::Exception::SyntaxError) {
433 LogError("Couldn't prepare select statement");
434 } Catch (SqlConnection::Exception::InternalError) {
435 LogError("Couldn't execute select statement");
437 ThrowMsg(DBCrypto::Exception::InternalError,
438 "Couldn't get Key for alias " << alias
439 << " using client label " << clnt_label);
442 void DBCrypto::getSingleType(
443 const std::string &clnt_label,
445 AliasVector& aliases) const
448 SqlConnection::DataCommandUniquePtr selectCommand =
449 m_connection->PrepareDataCommand(select_type_cross_cmd);
450 selectCommand->BindInteger(1, static_cast<int>(type));
451 selectCommand->BindString(2, clnt_label.c_str());
452 selectCommand->BindString(3, clnt_label.c_str());
454 while(selectCommand->Step()) {
456 alias = selectCommand->GetColumnString(0);
457 aliases.push_back(alias);
460 } Catch (SqlConnection::Exception::InvalidColumn) {
461 LogError("Select statement invalid column error");
462 } Catch (SqlConnection::Exception::SyntaxError) {
463 LogError("Couldn't prepare select statement");
464 } Catch (SqlConnection::Exception::InternalError) {
465 LogError("Couldn't execute select statement");
467 ThrowMsg(DBCrypto::Exception::InternalError,
468 "Couldn't get type " << static_cast<int>(type));
471 void DBCrypto::getAliases(
472 const std::string &clnt_label,
474 AliasVector& aliases)
476 getSingleType(clnt_label, type, aliases);
480 void DBCrypto::getKeyAliases(const std::string &clnt_label, AliasVector &aliases)
483 Transaction transaction(this);
484 SqlConnection::DataCommandUniquePtr selectCommand =
485 m_connection->PrepareDataCommand(select_key_type_cross_cmd);
486 selectCommand->BindInteger(1, static_cast<int>(DBDataType::DB_KEY_FIRST));
487 selectCommand->BindInteger(2, static_cast<int>(DBDataType::DB_KEY_LAST));
488 selectCommand->BindString(3, clnt_label.c_str());
489 selectCommand->BindString(4, clnt_label.c_str());
491 while(selectCommand->Step()) {
493 alias = selectCommand->GetColumnString(0);
494 aliases.push_back(alias);
496 transaction.commit();
498 } Catch (SqlConnection::Exception::InvalidColumn) {
499 LogError("Select statement invalid column error");
500 } Catch (SqlConnection::Exception::SyntaxError) {
501 LogError("Couldn't prepare select statement");
502 } Catch (SqlConnection::Exception::InternalError) {
503 LogError("Couldn't execute select statement");
505 ThrowMsg(DBCrypto::Exception::InternalError, "Couldn't get key aliases");
508 bool DBCrypto::deleteDBRow(const Alias &alias, const std::string &clnt_label)
511 Transaction transaction(this);
513 std::string owner_label = getLabelForAlias(alias);
514 if( ! owner_label.empty() )
516 // check access rights here
517 if( ! this->rowAccessControlCheck(alias, owner_label, clnt_label, DBCrypto::DB_OPERATION_REMOVE) )
518 ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested remove operation");
520 // if here, access right is granted - proceed with removal
521 // note: PERMISSION_TABLE entry will be deleted automatically by SQL (cascade relation between tables)
522 SqlConnection::DataCommandUniquePtr deleteCommand =
523 m_connection->PrepareDataCommand(delete_alias_cmd);
524 deleteCommand->BindString(1, alias.c_str());
525 deleteCommand->Step();
527 transaction.commit();
532 LogError("Error: no such alias: " << alias);
535 } Catch (SqlConnection::Exception::SyntaxError) {
536 LogError("Couldn't prepare delete statement");
537 } Catch (SqlConnection::Exception::InternalError) {
538 LogError("Couldn't execute delete statement");
540 ThrowMsg(DBCrypto::Exception::InternalError,
541 "Couldn't delete DBRow for alias " << alias << " using client label " << clnt_label);
544 void DBCrypto::saveKey(
545 const std::string& label,
546 const RawBuffer &key)
549 Transaction transaction(this);
550 SqlConnection::DataCommandUniquePtr insertCommand =
551 m_connection->PrepareDataCommand(insert_key_cmd);
552 insertCommand->BindString(1, label.c_str());
553 insertCommand->BindBlob(2, key);
554 insertCommand->Step();
555 transaction.commit();
557 } Catch (SqlConnection::Exception::SyntaxError) {
558 LogError("Couldn't prepare insert key statement");
559 } Catch (SqlConnection::Exception::InternalError) {
560 LogError("Couldn't execute insert statement");
562 ThrowMsg(DBCrypto::Exception::InternalError,
563 "Couldn't save key for label " << label);
566 DBCrypto::RawBufferOptional DBCrypto::getKey(
567 const std::string& label)
570 Transaction transaction(this);
571 SqlConnection::DataCommandUniquePtr selectCommand =
572 m_connection->PrepareDataCommand(select_key_cmd);
573 selectCommand->BindString(1, label.c_str());
575 if (selectCommand->Step()) {
576 transaction.commit();
577 return RawBufferOptional(
578 selectCommand->GetColumnBlob(0));
580 transaction.commit();
581 return RawBufferOptional();
584 } Catch (SqlConnection::Exception::InvalidColumn) {
585 LogError("Select statement invalid column error");
586 } Catch (SqlConnection::Exception::SyntaxError) {
587 LogError("Couldn't prepare insert key statement");
588 } Catch (SqlConnection::Exception::InternalError) {
589 LogError("Couldn't execute insert statement");
591 ThrowMsg(DBCrypto::Exception::InternalError,
592 "Couldn't get key for label " << label);
595 void DBCrypto::deleteKey(const std::string& label) {
597 Transaction transaction(this);
599 SqlConnection::DataCommandUniquePtr deleteCommand =
600 m_connection->PrepareDataCommand(delete_key_cmd);
601 deleteCommand->BindString(1, label.c_str());
602 deleteCommand->Step();
604 SqlConnection::DataCommandUniquePtr deleteData =
605 m_connection->PrepareDataCommand(delete_data_with_key_cmd);
606 deleteData->BindString(1, label.c_str());
609 transaction.commit();
611 } Catch (SqlConnection::Exception::SyntaxError) {
612 LogError("Couldn't prepare insert key statement");
613 } Catch (SqlConnection::Exception::InternalError) {
614 LogError("Couldn't execute insert statement");
616 ThrowMsg(DBCrypto::Exception::InternalError,
617 "Couldn't delete key for label " << label);
621 int DBCrypto::setAccessRights( const std::string& clnt_label,
623 const std::string& accessor_label,
624 const AccessRight value_to_set)
627 Transaction transaction(this);
629 // check if label is present
630 std::string owner_label = getLabelForAlias(alias);
631 if( ! owner_label.empty() )
633 // owner can not add permissions to itself
634 if(owner_label.compare(accessor_label) == 0)
635 ThrowMsg(Exception::InvalidArgs, "Invalid accessor label: equal to owner label");
637 // check access rights here - only owner can modify permissions
638 if(owner_label != clnt_label)
639 ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested write operation");
641 // if here, access right is granted - proceed to set permissions
642 SqlConnection::DataCommandUniquePtr setPermissionCommand =
643 m_connection->PrepareDataCommand(set_permission_alias_cmd);
644 setPermissionCommand->BindString(1, alias.c_str());
645 setPermissionCommand->BindString(2, accessor_label.c_str());
646 setPermissionCommand->BindString(3, toDBAccessRight(value_to_set));
647 setPermissionCommand->Step();
648 transaction.commit();
649 return CKM_API_SUCCESS;
653 LogError("Error: no such alias: " << alias);
654 return CKM_API_ERROR_DB_ALIAS_UNKNOWN;
656 } Catch (SqlConnection::Exception::SyntaxError) {
657 LogError("Couldn't prepare set statement");
658 } Catch (SqlConnection::Exception::InternalError) {
659 LogError("Couldn't execute set statement");
661 ThrowMsg(DBCrypto::Exception::InternalError,
662 "Couldn't set permissions for alias " << alias << " using client label " << clnt_label);
665 int DBCrypto::clearAccessRights(const std::string& clnt_label,
667 const std::string& accessor_label)
670 Transaction transaction(this);
672 std::string owner_label = getLabelForAlias(alias);
673 if( ! owner_label.empty() )
675 // check access rights here - only owner can modify permissions
676 if(owner_label != clnt_label)
677 ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested write operation");
679 // check if permission for <label, accessor_label> is defined - otherwise nothing to drop
680 if( this->getPermissionsForAliasAndLabel(alias, accessor_label).empty() )
681 ThrowMsg(Exception::InvalidArgs, "Permission not found");
683 // if here, access right is granted - proceed to delete permissions
684 SqlConnection::DataCommandUniquePtr deletePermissionCommand =
685 m_connection->PrepareDataCommand(delete_permission_cmd);
686 deletePermissionCommand->BindString(1, alias.c_str());
687 deletePermissionCommand->BindString(2, accessor_label.c_str());
688 deletePermissionCommand->Step();
689 transaction.commit();
690 return CKM_API_SUCCESS;
694 LogError("Error: no such alias: " << alias);
695 return CKM_API_ERROR_DB_ALIAS_UNKNOWN;
697 } Catch (SqlConnection::Exception::SyntaxError) {
698 LogError("Couldn't prepare delete statement");
699 } Catch (SqlConnection::Exception::InternalError) {
700 LogError("Couldn't execute delete statement");
702 ThrowMsg(DBCrypto::Exception::InternalError,
703 "Couldn't delete permissions for alias " << alias << " using client label " << clnt_label);
708 #pragma GCC diagnostic pop