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 *select_key_type_cmd =
85 "SELECT alias FROM CKM_TABLE WHERE "
91 const char *select_type_cmd =
93 "SELECT alias FROM CKM_TABLE WHERE dataType=?;";
95 const char *delete_alias_cmd =
97 "DELETE FROM CKM_TABLE WHERE alias=?;";
99 const char *delete_data_with_key_cmd =
101 "DELETE FROM CKM_TABLE WHERE label=?;";
103 // KEY_TABLE (label TEXT, key BLOB)
105 const char *db_create_key_cmd =
106 "CREATE TABLE KEY_TABLE("
107 " label TEXT PRIMARY KEY,"
111 const char *insert_key_cmd =
112 "INSERT INTO KEY_TABLE(label, key) VALUES (?, ?);";
113 const char *select_key_cmd =
114 "SELECT key FROM KEY_TABLE WHERE label=?;";
115 const char *delete_key_cmd =
116 "DELETE FROM KEY_TABLE WHERE label=?";
119 // PERMISSION_TABLE (label TEXT, label TEXT, access_flags TEXT)
121 const char *db_create_permission_cmd =
122 "CREATE TABLE PERMISSION_TABLE("
123 " alias TEXT NOT NULL,"
124 " label TEXT NOT NULL,"
125 " accessFlags TEXT NOT NULL,"
126 " FOREIGN KEY(alias) REFERENCES CKM_TABLE(alias) ON DELETE CASCADE,"
127 " PRIMARY KEY(label)"
130 const char *set_permission_alias_cmd =
131 "REPLACE INTO PERMISSION_TABLE(alias, label, accessFlags) VALUES (?, ?, ?);";
133 const char *select_permission_cmd =
135 "SELECT accessFlags FROM PERMISSION_TABLE WHERE alias=? AND label=?;";
137 const char *delete_permission_cmd =
139 "DELETE FROM PERMISSION_TABLE WHERE alias=? AND label=?;";
144 DBCrypto::DBCrypto(const std::string& path,
145 const RawBuffer &rawPass) {
147 m_inUserTransaction = false;
149 m_connection = new SqlConnection(path, SqlConnection::Flag::Option::CRW);
150 m_connection->SetKey(rawPass);
151 m_connection->ExecCommand("VACUUM;");
153 } Catch(SqlConnection::Exception::ConnectionBroken) {
154 LogError("Couldn't connect to database: " << path);
155 ReThrow(DBCrypto::Exception::InternalError);
156 } Catch(SqlConnection::Exception::InvalidArguments) {
157 LogError("Couldn't set the key for database");
158 ReThrow(DBCrypto::Exception::InternalError);
159 } Catch(SqlConnection::Exception::SyntaxError) {
160 LogError("Couldn't initiate the database");
161 ReThrow(DBCrypto::Exception::InternalError);
162 } Catch(SqlConnection::Exception::InternalError) {
163 LogError("Couldn't create the database");
164 ReThrow(DBCrypto::Exception::InternalError);
168 DBCrypto::DBCrypto(DBCrypto &&other) :
169 m_connection(other.m_connection),
170 m_inUserTransaction(other.m_inUserTransaction){
171 other.m_connection = NULL;
172 other.m_inUserTransaction = false;
175 DBCrypto::~DBCrypto() {
179 DBCrypto& DBCrypto::operator=(DBCrypto&& other) {
184 m_connection = other.m_connection;
185 other.m_connection = NULL;
187 m_inUserTransaction = other.m_inUserTransaction;
188 other.m_inUserTransaction = false;
193 void DBCrypto::createTable(
194 const char* create_cmd,
195 const char *table_name)
198 m_connection->ExecCommand(create_cmd);
199 } Catch(SqlConnection::Exception::SyntaxError) {
200 LogError("Couldn't create table : " << table_name << "!");
202 } Catch(SqlConnection::Exception::InternalError) {
203 LogError("Sqlite got into infinite busy state");
208 void DBCrypto::initDatabase() {
209 Transaction transaction(this);
210 if(!m_connection->CheckTableExist(main_table)) {
211 createTable(db_create_main_cmd, main_table);
213 if(!m_connection->CheckTableExist(key_table)) {
214 createTable(db_create_key_cmd, key_table);
216 if(!m_connection->CheckTableExist(permission_table)) {
217 createTable(db_create_permission_cmd, permission_table);
219 transaction.commit();
222 std::string DBCrypto::getLabelForAlias(const std::string& alias) const {
223 SqlConnection::DataCommandUniquePtr checkCmd =
224 m_connection->PrepareDataCommand(select_check_global_alias_cmd);
225 checkCmd->BindString(1, alias.c_str());
226 if(checkCmd->Step()) {
227 return checkCmd->GetColumnString(0);
229 return std::string();
231 bool DBCrypto::checkGlobalAliasExist(const std::string& alias) const {
232 std::string label = this->getLabelForAlias(alias);
233 if(label.empty() == false) {
234 LogDebug("Global alias '" << alias << "' exists already for label " << label);
240 bool DBCrypto::checkAliasExist(const std::string& alias) const {
241 SqlConnection::DataCommandUniquePtr checkCmd =
242 m_connection->PrepareDataCommand(select_check_alias_cmd);
243 checkCmd->BindString(1, alias.c_str());
244 if(checkCmd->Step()) {
245 LogDebug("Private alias '" << alias << "' exists already for type "
246 << checkCmd->GetColumnInteger(0));
252 void DBCrypto::saveDBRow(const DBRow &row){
255 //Sqlite does not support partial index in our version,
256 //so we do it by hand
257 Transaction transaction(this);
258 if(checkAliasExist(row.alias)) {
259 ThrowMsg(DBCrypto::Exception::AliasExists,
260 "Alias exists for alias: " << row.alias);
263 SqlConnection::DataCommandUniquePtr insertCommand =
264 m_connection->PrepareDataCommand(insert_main_cmd);
265 insertCommand->BindString(1, row.alias.c_str());
266 insertCommand->BindString(2, row.smackLabel.c_str());
267 insertCommand->BindInteger(3, row.exportable);
268 insertCommand->BindInteger(4, static_cast<int>(row.dataType));
269 insertCommand->BindInteger(5, static_cast<int>(row.algorithmType));
270 insertCommand->BindInteger(6, row.encryptionScheme);
271 insertCommand->BindBlob(7, row.iv);
272 insertCommand->BindInteger(8, row.dataSize);
273 insertCommand->BindBlob(9, row.data);
274 insertCommand->BindBlob(10, row.tag);
276 insertCommand->Step();
277 transaction.commit();
280 } Catch(SqlConnection::Exception::SyntaxError) {
281 LogError("Couldn't prepare insert statement");
282 } Catch(SqlConnection::Exception::InternalError) {
283 LogError("Couldn't execute insert statement");
285 ThrowMsg(DBCrypto::Exception::InternalError,
286 "Couldn't save DBRow");
289 DBRow DBCrypto::getRow(const SqlConnection::DataCommandUniquePtr &selectCommand) {
291 row.alias = selectCommand->GetColumnString(0);
292 row.smackLabel = selectCommand->GetColumnString(1);
293 row.exportable = selectCommand->GetColumnInteger(2);
294 row.dataType = static_cast<DBDataType>(selectCommand->GetColumnInteger(3));
295 row.algorithmType = static_cast<DBCMAlgType>(selectCommand->GetColumnInteger(4));
296 row.encryptionScheme = selectCommand->GetColumnInteger(5);
297 row.iv = selectCommand->GetColumnBlob(6);
298 row.dataSize = selectCommand->GetColumnInteger(7);
299 row.data = selectCommand->GetColumnBlob(8);
300 row.tag = selectCommand->GetColumnBlob(9);
304 std::string DBCrypto::getPermissionsForAliasAndLabel(const Alias &alias, const std::string &label) const
307 SqlConnection::DataCommandUniquePtr selectCommand =
308 m_connection->PrepareDataCommand(select_permission_cmd);
309 selectCommand->BindString(1, alias.c_str());
310 selectCommand->BindString(2, label.c_str());
312 if(selectCommand->Step())
313 return selectCommand->GetColumnString(0);
315 return std::string();
316 } Catch (SqlConnection::Exception::InvalidColumn) {
317 LogError("Select statement invalid column error");
318 } Catch (SqlConnection::Exception::SyntaxError) {
319 LogError("Couldn't prepare select statement");
320 } Catch (SqlConnection::Exception::InternalError) {
321 LogError("Couldn't execute select statement");
323 return std::string();
327 bool DBCrypto::rowAccessControlCheck(const Alias &alias,
328 const std::string &owner_label,
329 const std::string &clnt_label,
330 DBCrypto::DBOperationType access_type) const
332 // owner of the entry have all the permissions by default
333 // check if requesting client is the entry owner - if so, exit (permission granted)
334 if(owner_label == clnt_label)
337 // perform permissions DB query
338 std::string permission_string = this->getPermissionsForAliasAndLabel(alias, clnt_label);
340 // check if requested operation is in the permission string
341 LogDebug("pair <" << alias << "," << clnt_label << "> permission rights: \"" << permission_string << "\"");
342 if(permission_string.find(access_type) != std::string::npos)
347 bool DBCrypto::rowAccessControlCheck(const DBRow & input_row,
348 const std::string &clnt_label,
349 DBCrypto::DBOperationType access_type) const
351 return this->rowAccessControlCheck(input_row.alias, input_row.smackLabel, clnt_label, access_type);
355 DBCrypto::DBRowOptional DBCrypto::getDBRow(
357 const std::string &clnt_label,
361 Transaction transaction(this);
362 SqlConnection::DataCommandUniquePtr selectCommand =
363 m_connection->PrepareDataCommand(select_alias_cmd);
364 selectCommand->BindString(1, alias.c_str());
365 selectCommand->BindInteger(2, static_cast<int>(type));
367 if(selectCommand->Step())
370 DBRow current_row = getRow(selectCommand);
372 // check access rights here
373 if( ! this->rowAccessControlCheck(current_row, clnt_label, DBCrypto::DB_OPERATION_READ) )
374 ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested operation");
376 // finalize DB operations
377 transaction.commit();
380 return DBRowOptional(current_row);
382 return DBRowOptional();
384 } Catch (SqlConnection::Exception::InvalidColumn) {
385 LogError("Select statement invalid column error");
386 } Catch (SqlConnection::Exception::SyntaxError) {
387 LogError("Couldn't prepare select statement");
388 } Catch (SqlConnection::Exception::InternalError) {
389 LogError("Couldn't execute select statement");
391 ThrowMsg(DBCrypto::Exception::InternalError,
392 "Couldn't get row for type " << static_cast<int>(type) <<
393 " alias " << alias << " using client label " << clnt_label);
396 DBCrypto::DBRowOptional DBCrypto::getKeyDBRow(
398 const std::string &clnt_label)
401 Transaction transaction(this);
402 SqlConnection::DataCommandUniquePtr selectCommand =
403 m_connection->PrepareDataCommand(select_key_alias_cmd);
404 selectCommand->BindString(1, alias.c_str());
405 selectCommand->BindInteger(2, static_cast<int>(DBDataType::DB_KEY_FIRST));
406 selectCommand->BindInteger(3, static_cast<int>(DBDataType::DB_KEY_LAST));
408 if(selectCommand->Step())
411 DBRow current_row = getRow(selectCommand);
413 // check access rights here
414 if( ! this->rowAccessControlCheck(current_row, clnt_label, DBCrypto::DB_OPERATION_READ) )
415 ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested operation");
417 // finalize DB operations
418 transaction.commit();
421 return DBRowOptional(current_row);
423 return DBRowOptional();
425 } Catch (SqlConnection::Exception::InvalidColumn) {
426 LogError("Select statement invalid column error");
427 } Catch (SqlConnection::Exception::SyntaxError) {
428 LogError("Couldn't prepare select statement");
429 } Catch (SqlConnection::Exception::InternalError) {
430 LogError("Couldn't execute select statement");
432 ThrowMsg(DBCrypto::Exception::InternalError,
433 "Couldn't get Key for alias " << alias
434 << " using client label " << clnt_label);
437 void DBCrypto::getSingleType(
439 AliasVector& aliases) const
442 SqlConnection::DataCommandUniquePtr selectCommand =
443 m_connection->PrepareDataCommand(select_type_cmd);
444 selectCommand->BindInteger(1, static_cast<int>(type));
446 while(selectCommand->Step()) {
448 alias = selectCommand->GetColumnString(0);
449 aliases.push_back(alias);
452 } Catch (SqlConnection::Exception::InvalidColumn) {
453 LogError("Select statement invalid column error");
454 } Catch (SqlConnection::Exception::SyntaxError) {
455 LogError("Couldn't prepare select statement");
456 } Catch (SqlConnection::Exception::InternalError) {
457 LogError("Couldn't execute select statement");
459 ThrowMsg(DBCrypto::Exception::InternalError,
460 "Couldn't get type " << static_cast<int>(type));
463 void DBCrypto::getAliases(
465 AliasVector& aliases)
467 getSingleType(type, aliases);
471 void DBCrypto::getKeyAliases(AliasVector &aliases)
474 Transaction transaction(this);
475 SqlConnection::DataCommandUniquePtr selectCommand =
476 m_connection->PrepareDataCommand(select_key_type_cmd);
477 selectCommand->BindInteger(1, static_cast<int>(DBDataType::DB_KEY_FIRST));
478 selectCommand->BindInteger(2, static_cast<int>(DBDataType::DB_KEY_LAST));
480 while(selectCommand->Step()) {
482 alias = selectCommand->GetColumnString(0);
483 aliases.push_back(alias);
485 transaction.commit();
487 } Catch (SqlConnection::Exception::InvalidColumn) {
488 LogError("Select statement invalid column error");
489 } Catch (SqlConnection::Exception::SyntaxError) {
490 LogError("Couldn't prepare select statement");
491 } Catch (SqlConnection::Exception::InternalError) {
492 LogError("Couldn't execute select statement");
494 ThrowMsg(DBCrypto::Exception::InternalError, "Couldn't get key aliases");
497 bool DBCrypto::deleteDBRow(const Alias &alias, const std::string &clnt_label)
500 Transaction transaction(this);
502 std::string owner_label = getLabelForAlias(alias);
503 if( ! owner_label.empty() )
505 // check access rights here
506 if( ! this->rowAccessControlCheck(alias, owner_label, clnt_label, DBCrypto::DB_OPERATION_REMOVE) )
507 ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested remove operation");
509 // if here, access right is granted - proceed with removal
510 // note: PERMISSION_TABLE entry will be deleted automatically by SQL (cascade relation between tables)
511 SqlConnection::DataCommandUniquePtr deleteCommand =
512 m_connection->PrepareDataCommand(delete_alias_cmd);
513 deleteCommand->BindString(1, alias.c_str());
514 deleteCommand->Step();
516 transaction.commit();
521 LogError("Error: no such alias: " << alias);
524 } Catch (SqlConnection::Exception::SyntaxError) {
525 LogError("Couldn't prepare delete statement");
526 } Catch (SqlConnection::Exception::InternalError) {
527 LogError("Couldn't execute delete statement");
529 ThrowMsg(DBCrypto::Exception::InternalError,
530 "Couldn't delete DBRow for alias " << alias << " using client label " << clnt_label);
533 void DBCrypto::saveKey(
534 const std::string& label,
535 const RawBuffer &key)
538 Transaction transaction(this);
539 SqlConnection::DataCommandUniquePtr insertCommand =
540 m_connection->PrepareDataCommand(insert_key_cmd);
541 insertCommand->BindString(1, label.c_str());
542 insertCommand->BindBlob(2, key);
543 insertCommand->Step();
544 transaction.commit();
546 } Catch (SqlConnection::Exception::SyntaxError) {
547 LogError("Couldn't prepare insert key statement");
548 } Catch (SqlConnection::Exception::InternalError) {
549 LogError("Couldn't execute insert statement");
551 ThrowMsg(DBCrypto::Exception::InternalError,
552 "Couldn't save key for label " << label);
555 DBCrypto::RawBufferOptional DBCrypto::getKey(
556 const std::string& label)
559 Transaction transaction(this);
560 SqlConnection::DataCommandUniquePtr selectCommand =
561 m_connection->PrepareDataCommand(select_key_cmd);
562 selectCommand->BindString(1, label.c_str());
564 if (selectCommand->Step()) {
565 transaction.commit();
566 return RawBufferOptional(
567 selectCommand->GetColumnBlob(0));
569 transaction.commit();
570 return RawBufferOptional();
573 } Catch (SqlConnection::Exception::InvalidColumn) {
574 LogError("Select statement invalid column error");
575 } Catch (SqlConnection::Exception::SyntaxError) {
576 LogError("Couldn't prepare insert key statement");
577 } Catch (SqlConnection::Exception::InternalError) {
578 LogError("Couldn't execute insert statement");
580 ThrowMsg(DBCrypto::Exception::InternalError,
581 "Couldn't get key for label " << label);
584 void DBCrypto::deleteKey(const std::string& label) {
586 Transaction transaction(this);
588 SqlConnection::DataCommandUniquePtr deleteCommand =
589 m_connection->PrepareDataCommand(delete_key_cmd);
590 deleteCommand->BindString(1, label.c_str());
591 deleteCommand->Step();
593 SqlConnection::DataCommandUniquePtr deleteData =
594 m_connection->PrepareDataCommand(delete_data_with_key_cmd);
595 deleteData->BindString(1, label.c_str());
598 transaction.commit();
600 } Catch (SqlConnection::Exception::SyntaxError) {
601 LogError("Couldn't prepare insert key statement");
602 } Catch (SqlConnection::Exception::InternalError) {
603 LogError("Couldn't execute insert statement");
605 ThrowMsg(DBCrypto::Exception::InternalError,
606 "Couldn't delete key for label " << label);
610 int DBCrypto::setAccessRights( const std::string& clnt_label,
612 const std::string& accessor_label,
613 const AccessRight value_to_set)
616 Transaction transaction(this);
618 // check if label is present
619 std::string owner_label = getLabelForAlias(alias);
620 if( ! owner_label.empty() )
622 // owner can not add permissions to itself
623 if(owner_label.compare(accessor_label) == 0)
624 ThrowMsg(Exception::InvalidArgs, "Invalid accessor label: equal to owner label");
626 // check access rights here - only owner can modify permissions
627 if(owner_label != clnt_label)
628 ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested write operation");
630 // if here, access right is granted - proceed to set permissions
631 SqlConnection::DataCommandUniquePtr setPermissionCommand =
632 m_connection->PrepareDataCommand(set_permission_alias_cmd);
633 setPermissionCommand->BindString(1, alias.c_str());
634 setPermissionCommand->BindString(2, accessor_label.c_str());
635 setPermissionCommand->BindString(3, toDBAccessRight(value_to_set));
636 setPermissionCommand->Step();
637 transaction.commit();
638 return CKM_API_SUCCESS;
642 LogError("Error: no such alias: " << alias);
643 return CKM_API_ERROR_DB_ALIAS_UNKNOWN;
645 } Catch (SqlConnection::Exception::SyntaxError) {
646 LogError("Couldn't prepare set statement");
647 } Catch (SqlConnection::Exception::InternalError) {
648 LogError("Couldn't execute set statement");
650 ThrowMsg(DBCrypto::Exception::InternalError,
651 "Couldn't set permissions for alias " << alias << " using client label " << clnt_label);
654 int DBCrypto::clearAccessRights(const std::string& clnt_label,
656 const std::string& accessor_label)
659 Transaction transaction(this);
661 std::string owner_label = getLabelForAlias(alias);
662 if( ! owner_label.empty() )
664 // check access rights here - only owner can modify permissions
665 if(owner_label != clnt_label)
666 ThrowMsg(Exception::PermissionDenied, "Not enough permissions to perform requested write operation");
668 // check if permission for <label, accessor_label> is defined - otherwise nothing to drop
669 if( this->getPermissionsForAliasAndLabel(alias, accessor_label).empty() )
670 ThrowMsg(Exception::InvalidArgs, "Permission not found");
672 // if here, access right is granted - proceed to delete permissions
673 SqlConnection::DataCommandUniquePtr deletePermissionCommand =
674 m_connection->PrepareDataCommand(delete_permission_cmd);
675 deletePermissionCommand->BindString(1, alias.c_str());
676 deletePermissionCommand->BindString(2, accessor_label.c_str());
677 deletePermissionCommand->Step();
678 transaction.commit();
679 return CKM_API_SUCCESS;
683 LogError("Error: no such alias: " << alias);
684 return CKM_API_ERROR_DB_ALIAS_UNKNOWN;
686 } Catch (SqlConnection::Exception::SyntaxError) {
687 LogError("Couldn't prepare delete statement");
688 } Catch (SqlConnection::Exception::InternalError) {
689 LogError("Couldn't execute delete statement");
691 ThrowMsg(DBCrypto::Exception::InternalError,
692 "Couldn't delete permissions for alias " << alias << " using client label " << clnt_label);
697 #pragma GCC diagnostic pop