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 *TABLE_NAME = "NAME_TABLE";
33 const char *TABLE_OBJECT = "OBJECT_TABLE";
34 const char *TABLE_KEY = "KEY_TABLE";
35 const char *TABLE_PERMISSION = "PERMISSION_TABLE";
37 const char *DB_CMD_NAME_CREATE =
38 "CREATE TABLE NAME_TABLE("
39 " name TEXT NOT NULL,"
40 " label TEXT NOT NULL,"
41 " idx INTEGER PRIMARY KEY AUTOINCREMENT,"
42 " UNIQUE(name, label)"
43 "); CREATE INDEX name_index_idx ON NAME_TABLE(idx);";
45 const char *DB_CMD_NAME_INSERT =
46 "INSERT INTO NAME_TABLE("
48 " VALUES(?101, ?102);";
50 // any idea how to do it using constexpr ?
51 #define DB_CMD_NAME_LOOKUP_IDX \
52 "(SELECT idx FROM NAME_TABLE WHERE name=?101 and label=?102)"
54 const char *DB_CMD_NAME_COUNT_ROWS =
55 "SELECT COUNT(idx) FROM NAME_TABLE WHERE name=?101 AND label=?102;";
57 const char *DB_CMD_NAME_DELETE =
58 "DELETE FROM NAME_TABLE WHERE name=?101 AND label=?102;";
60 const char *DB_CMD_NAME_DELETE_BY_LABEL =
61 "DELETE FROM NAME_TABLE WHERE label=?102;";
63 const char *DB_CMD_OBJECT_CREATE =
64 "CREATE TABLE OBJECT_TABLE("
65 " exportable INTEGER NOT NULL,"
66 " dataType INTEGER NOT NULL,"
67 " algorithmType INTEGER NOT NULL,"
68 " encryptionScheme INTEGER NOT NULL,"
70 " dataSize INTEGER NOT NULL,"
71 " data BLOB NOT NULL,"
73 " idx INTEGER NOT NULL,"
74 " FOREIGN KEY(idx) REFERENCES NAME_TABLE(idx) ON DELETE CASCADE,"
75 " PRIMARY KEY(idx, dataType)"
76 ");"; // TODO: index and performance tests
78 const char *DB_CMD_OBJECT_INSERT =
79 "INSERT INTO OBJECT_TABLE("
80 " exportable, dataType,"
81 " algorithmType, encryptionScheme,"
82 " iv, dataSize, data, tag, idx) "
83 " VALUES(?001, ?002, ?003, ?004, ?005, "
84 " ?006, ?007, ?008, " DB_CMD_NAME_LOOKUP_IDX ");";
86 const char *DB_CMD_OBJECT_SELECT_BY_NAME_AND_LABEL =
87 "SELECT * FROM OBJECT_TABLE WHERE "
88 " (dataType BETWEEN ?001 AND ?002) AND "
89 " idx=" DB_CMD_NAME_LOOKUP_IDX ";";
92 const char *DB_CMD_KEY_CREATE =
93 "CREATE TABLE KEY_TABLE("
94 " label TEXT PRIMARY KEY,"
98 const char *DB_CMD_KEY_INSERT =
99 "INSERT INTO KEY_TABLE(label, key) VALUES (?, ?);";
100 const char *DB_CMD_KEY_SELECT =
101 "SELECT key FROM KEY_TABLE WHERE label=?;";
102 const char *DB_CMD_KEY_DELETE =
103 "DELETE FROM KEY_TABLE WHERE label=?";
106 const char *DB_CMD_PERMISSION_CREATE =
107 "CREATE TABLE PERMISSION_TABLE("
108 " label TEXT NOT NULL,"
109 " accessFlags TEXT NOT NULL,"
110 " idx INTEGER NOT NULL,"
111 " FOREIGN KEY(idx) REFERENCES NAME_TABLE(idx) ON DELETE CASCADE,"
112 " PRIMARY KEY(label, idx)"
113 "); CREATE INDEX perm_index_idx ON PERMISSION_TABLE(idx);"; // based on ANALYZE and performance test result
115 const char *DB_CMD_PERMISSION_SET =
116 "REPLACE INTO PERMISSION_TABLE(label, accessFlags, idx) "
117 " VALUES (?001, ?002, " DB_CMD_NAME_LOOKUP_IDX ");";
119 const char *DB_CMD_PERMISSION_SELECT =
120 "SELECT accessFlags FROM PERMISSION_TABLE WHERE label=?001 AND "
121 " idx=" DB_CMD_NAME_LOOKUP_IDX ";";
123 const char *DB_CMD_PERMISSION_DELETE =
124 "DELETE FROM PERMISSION_TABLE WHERE label=?001 AND "
125 " idx=" DB_CMD_NAME_LOOKUP_IDX ";";
129 * GROUP BY is necessary because of the following case:
130 * -There are several permissions to L1, N1 (label, name) from other accessors. When listing
131 * objects accessible by L1 the query will produce one result (L1, N1) for each allowed
132 * accessor but GROUP BY will reduce them to one so L1 will have (L1, N1) on its list only once
134 const char *DB_CMD_NAME_SELECT_BY_TYPE_AND_PERMISSION =
135 "SELECT N.label, N.name FROM NAME_TABLE AS N "
136 " JOIN OBJECT_TABLE AS O ON O.idx=N.idx "
137 " LEFT JOIN PERMISSION_TABLE AS P ON P.idx=N.idx "
138 " WHERE O.dataType>=?001 AND O.dataType<=?002 AND "
139 " ((N.label=?003) OR (P.label=?003 AND P.accessFlags IS NOT NULL)) GROUP BY N.idx;";
144 DBCrypto::DBCrypto(const std::string& path, 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)
172 other.m_connection = NULL;
173 other.m_inUserTransaction = false;
176 DBCrypto::~DBCrypto() {
180 DBCrypto& DBCrypto::operator=(DBCrypto&& other) {
185 m_connection = other.m_connection;
186 other.m_connection = NULL;
188 m_inUserTransaction = other.m_inUserTransaction;
189 other.m_inUserTransaction = false;
194 void DBCrypto::createTable(
195 const char* create_cmd,
196 const char *table_name)
199 m_connection->ExecCommand(create_cmd);
200 } Catch(SqlConnection::Exception::SyntaxError) {
201 LogError("Couldn't create table : " << table_name << "!");
203 } Catch(SqlConnection::Exception::InternalError) {
204 LogError("Sqlite got into infinite busy state");
209 void DBCrypto::initDatabase() {
210 Transaction transaction(this);
211 if(!m_connection->CheckTableExist(TABLE_NAME)) {
212 createTable(DB_CMD_NAME_CREATE, TABLE_NAME);
214 if(!m_connection->CheckTableExist(TABLE_OBJECT)) {
215 createTable(DB_CMD_OBJECT_CREATE, TABLE_OBJECT);
217 if(!m_connection->CheckTableExist(TABLE_KEY)) {
218 createTable(DB_CMD_KEY_CREATE, TABLE_KEY);
220 if(!m_connection->CheckTableExist(TABLE_PERMISSION)) {
221 createTable(DB_CMD_PERMISSION_CREATE, TABLE_PERMISSION);
223 transaction.commit();
226 bool DBCrypto::isNameLabelPresent(const Name &name, const Label &owner) const {
228 NameTable nameTable(this->m_connection);
229 return nameTable.isPresent(name, owner);
230 } Catch(SqlConnection::Exception::SyntaxError) {
231 LogError("Couldn't prepare insert statement");
232 } Catch(SqlConnection::Exception::InternalError) {
233 LogError("Couldn't execute insert statement");
235 ThrowMsg(DBCrypto::Exception::InternalError,
236 "Couldn't check if name and label pair is present");
239 void DBCrypto::saveDBRow(const DBRow &row){
241 // transaction is present in the layer above
242 NameTable nameTable(this->m_connection);
243 ObjectTable objectTable(this->m_connection);
244 nameTable.addRow(row.name, row.ownerLabel);
245 objectTable.addRow(row);
247 } Catch(SqlConnection::Exception::SyntaxError) {
248 LogError("Couldn't prepare insert statement");
249 } Catch(SqlConnection::Exception::InternalError) {
250 LogError("Couldn't execute insert statement");
252 ThrowMsg(DBCrypto::Exception::InternalError,
253 "Couldn't save DBRow");
256 bool DBCrypto::deleteDBRow(
258 const Label &ownerLabel)
261 // transaction is present in the layer above
262 NameTable nameTable(this->m_connection);
263 if(nameTable.isPresent(name, ownerLabel))
265 nameTable.deleteRow(name, ownerLabel);
269 } Catch (SqlConnection::Exception::SyntaxError) {
270 LogError("Couldn't prepare delete statement");
271 } Catch (SqlConnection::Exception::InternalError) {
272 LogError("Couldn't execute delete statement");
274 ThrowMsg(DBCrypto::Exception::InternalError,
275 "Couldn't delete DBRow for name " << name << " using ownerLabel " << ownerLabel);
278 DBRow DBCrypto::getRow(
280 const Label &ownerLabel,
281 const SqlConnection::DataCommandUniquePtr &selectCommand) const {
284 row.ownerLabel = ownerLabel;
285 row.exportable = selectCommand->GetColumnInteger(0);
286 row.dataType = DBDataType(selectCommand->GetColumnInteger(1));
287 row.algorithmType = static_cast<DBCMAlgType>(selectCommand->GetColumnInteger(2));
288 row.encryptionScheme = selectCommand->GetColumnInteger(3);
289 row.iv = selectCommand->GetColumnBlob(4);
290 row.dataSize = selectCommand->GetColumnInteger(5);
291 row.data = selectCommand->GetColumnBlob(6);
292 row.tag = selectCommand->GetColumnBlob(7);
296 PermissionOptional DBCrypto::getPermissionRow(
298 const Label &ownerLabel,
299 const Label &accessorLabel) const
302 PermissionTable permissionTable(this->m_connection);
303 return permissionTable.getPermissionRow(name, ownerLabel, accessorLabel);
304 } Catch (SqlConnection::Exception::InvalidColumn) {
305 LogError("Select statement invalid column error");
306 } Catch (SqlConnection::Exception::SyntaxError) {
307 LogError("Couldn't prepare select statement");
308 } Catch (SqlConnection::Exception::InternalError) {
309 LogError("Couldn't execute select statement");
311 return PermissionOptional();
314 DBCrypto::DBRowOptional DBCrypto::getDBRow(
316 const Label &ownerLabel,
319 return getDBRow(name, ownerLabel, type, type);
322 DBCrypto::DBRowOptional DBCrypto::getDBRow(
324 const Label &ownerLabel,
325 DBDataType typeRangeStart,
326 DBDataType typeRangeStop)
329 SqlConnection::DataCommandUniquePtr selectCommand =
330 m_connection->PrepareDataCommand(DB_CMD_OBJECT_SELECT_BY_NAME_AND_LABEL);
331 selectCommand->BindInteger(1, typeRangeStart);
332 selectCommand->BindInteger(2, typeRangeStop);
334 // name table reference
335 selectCommand->BindString (101, name.c_str());
336 selectCommand->BindString (102, ownerLabel.c_str());
338 if(selectCommand->Step())
341 DBRow current_row = getRow(name, ownerLabel, selectCommand);
344 return DBRowOptional(current_row);
346 return DBRowOptional();
348 } Catch (SqlConnection::Exception::InvalidColumn) {
349 LogError("Select statement invalid column error");
350 } Catch (SqlConnection::Exception::SyntaxError) {
351 LogError("Couldn't prepare select statement");
352 } Catch (SqlConnection::Exception::InternalError) {
353 LogError("Couldn't execute select statement");
355 ThrowMsg(DBCrypto::Exception::InternalError,
356 "Couldn't get row of type <" <<
357 static_cast<int>(typeRangeStart) << "," <<
358 static_cast<int>(typeRangeStop) << ">" <<
359 " name " << name << " with owner label " << ownerLabel);
362 void DBCrypto::listNames(
363 const Label &smackLabel,
364 LabelNameVector& labelNameVector,
367 listNames(smackLabel, labelNameVector, type, type);
370 void DBCrypto::listNames(
371 const Label &smackLabel,
372 LabelNameVector& labelNameVector,
373 DBDataType typeRangeStart,
374 DBDataType typeRangeStop)
377 Transaction transaction(this);
378 SqlConnection::DataCommandUniquePtr selectCommand =
379 m_connection->PrepareDataCommand(DB_CMD_NAME_SELECT_BY_TYPE_AND_PERMISSION);
380 selectCommand->BindInteger(1, static_cast<int>(typeRangeStart));
381 selectCommand->BindInteger(2, static_cast<int>(typeRangeStop));
382 selectCommand->BindString(3, smackLabel.c_str());
384 while(selectCommand->Step()) {
385 Label ownerLabel = selectCommand->GetColumnString(0);
386 Name name = selectCommand->GetColumnString(1);
387 labelNameVector.push_back(std::make_pair(ownerLabel, name));
390 } Catch (SqlConnection::Exception::InvalidColumn) {
391 LogError("Select statement invalid column error");
392 } Catch (SqlConnection::Exception::SyntaxError) {
393 LogError("Couldn't prepare select statement");
394 } Catch (SqlConnection::Exception::InternalError) {
395 LogError("Couldn't execute select statement");
397 ThrowMsg(DBCrypto::Exception::InternalError,
398 "Couldn't list names of type <" <<
399 static_cast<int>(typeRangeStart) << "," <<
400 static_cast<int>(typeRangeStop) << ">" <<
401 " accessible to client label " << smackLabel);
406 void DBCrypto::saveKey(
408 const RawBuffer &key)
411 SqlConnection::DataCommandUniquePtr insertCommand =
412 m_connection->PrepareDataCommand(DB_CMD_KEY_INSERT);
413 insertCommand->BindString(1, label.c_str());
414 insertCommand->BindBlob(2, key);
415 insertCommand->Step();
417 } Catch (SqlConnection::Exception::SyntaxError) {
418 LogError("Couldn't prepare insert key statement");
419 } Catch (SqlConnection::Exception::InternalError) {
420 LogError("Couldn't execute insert statement");
422 ThrowMsg(DBCrypto::Exception::InternalError,
423 "Couldn't save key for label " << label);
426 DBCrypto::RawBufferOptional DBCrypto::getKey(const Label& label)
429 SqlConnection::DataCommandUniquePtr selectCommand =
430 m_connection->PrepareDataCommand(DB_CMD_KEY_SELECT);
431 selectCommand->BindString(1, label.c_str());
433 if (selectCommand->Step()) {
434 return RawBufferOptional(
435 selectCommand->GetColumnBlob(0));
437 return RawBufferOptional();
440 } Catch (SqlConnection::Exception::InvalidColumn) {
441 LogError("Select statement invalid column error");
442 } Catch (SqlConnection::Exception::SyntaxError) {
443 LogError("Couldn't prepare insert key statement");
444 } Catch (SqlConnection::Exception::InternalError) {
445 LogError("Couldn't execute insert statement");
447 ThrowMsg(DBCrypto::Exception::InternalError,
448 "Couldn't get key for label " << label);
451 void DBCrypto::deleteKey(const Label& label) {
453 Transaction transaction(this);
455 SqlConnection::DataCommandUniquePtr deleteCommand =
456 m_connection->PrepareDataCommand(DB_CMD_KEY_DELETE);
457 deleteCommand->BindString(1, label.c_str());
458 deleteCommand->Step();
460 NameTable nameTable(this->m_connection);
461 nameTable.deleteAllRows(label);
463 transaction.commit();
465 } Catch (SqlConnection::Exception::SyntaxError) {
466 LogError("Couldn't prepare insert key statement");
467 } Catch (SqlConnection::Exception::InternalError) {
468 LogError("Couldn't execute insert statement");
470 ThrowMsg(DBCrypto::Exception::InternalError,
471 "Couldn't delete key for label " << label);
474 void DBCrypto::setPermission(
476 const Label& ownerLabel,
477 const Label& accessorLabel,
478 const Permission permissions)
481 PermissionTable permissionTable(this->m_connection);
482 permissionTable.setPermission(name, ownerLabel, accessorLabel, permissions);
484 } Catch (SqlConnection::Exception::SyntaxError) {
485 LogError("Couldn't prepare set statement");
486 } Catch (SqlConnection::Exception::InternalError) {
487 LogError("Couldn't execute set statement");
489 ThrowMsg(DBCrypto::Exception::InternalError,
490 "Couldn't set permissions for name " << name );
493 void DBCrypto::PermissionTable::setPermission(
495 const Label& ownerLabel,
496 const Label& accessorLabel,
497 const Permission permissions)
499 if(permissions == Permission::NONE)
502 SqlConnection::DataCommandUniquePtr deletePermissionCommand =
503 m_connection->PrepareDataCommand(DB_CMD_PERMISSION_DELETE);
504 deletePermissionCommand->BindString(1, accessorLabel.c_str());
505 deletePermissionCommand->BindString(101, name.c_str());
506 deletePermissionCommand->BindString(102, ownerLabel.c_str());
507 deletePermissionCommand->Step();
511 // add new permissions
512 SqlConnection::DataCommandUniquePtr setPermissionCommand =
513 m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SET);
514 setPermissionCommand->BindString(1, accessorLabel.c_str());
515 setPermissionCommand->BindString(2, toDBPermission(permissions));
516 setPermissionCommand->BindString(101, name.c_str());
517 setPermissionCommand->BindString(102, ownerLabel.c_str());
518 setPermissionCommand->Step();
522 PermissionOptional DBCrypto::PermissionTable::getPermissionRow(
524 const Label &ownerLabel,
525 const Label &accessorLabel) const
527 SqlConnection::DataCommandUniquePtr selectCommand =
528 m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SELECT);
529 selectCommand->BindString(1, accessorLabel.c_str());
531 // name table reference
532 selectCommand->BindString(101, name.c_str());
533 selectCommand->BindString(102, ownerLabel.c_str());
535 if(selectCommand->Step())
537 // there is entry for the <name, ownerLabel> pair
538 return PermissionOptional(toPermission(selectCommand->GetColumnString(0)));
540 return PermissionOptional();
543 void DBCrypto::NameTable::addRow(
545 const Label &ownerLabel)
547 // insert NAME_TABLE item
548 SqlConnection::DataCommandUniquePtr insertNameCommand =
549 m_connection->PrepareDataCommand(DB_CMD_NAME_INSERT);
550 insertNameCommand->BindString (101, name.c_str());
551 insertNameCommand->BindString (102, ownerLabel.c_str());
552 insertNameCommand->Step();
555 void DBCrypto::NameTable::deleteRow(
557 const Label &ownerLabel)
559 SqlConnection::DataCommandUniquePtr deleteCommand =
560 m_connection->PrepareDataCommand(DB_CMD_NAME_DELETE);
561 deleteCommand->BindString(101, name.c_str());
562 deleteCommand->BindString(102, ownerLabel.c_str());
564 // Step() result code does not provide information whether
565 // anything was removed.
566 deleteCommand->Step();
569 void DBCrypto::NameTable::deleteAllRows(const Label &ownerLabel)
571 SqlConnection::DataCommandUniquePtr deleteData =
572 m_connection->PrepareDataCommand(DB_CMD_NAME_DELETE_BY_LABEL);
573 deleteData->BindString(102, ownerLabel.c_str());
575 // Step() result code does not provide information whether
576 // anything was removed.
580 bool DBCrypto::NameTable::isPresent(const Name &name, const Label &ownerLabel) const
582 SqlConnection::DataCommandUniquePtr checkCmd =
583 m_connection->PrepareDataCommand(DB_CMD_NAME_COUNT_ROWS);
584 checkCmd->BindString(101, name.c_str());
585 checkCmd->BindString(102, ownerLabel.c_str());
586 if(checkCmd->Step()) {
587 int element_count = checkCmd->GetColumnInteger(0);
588 LogDebug("Item name: " << name << " ownerLabel: " << ownerLabel <<
589 " hit count: " << element_count);
590 if(element_count > 0)
596 void DBCrypto::ObjectTable::addRow(const DBRow &row)
598 SqlConnection::DataCommandUniquePtr insertObjectCommand =
599 m_connection->PrepareDataCommand(DB_CMD_OBJECT_INSERT);
600 insertObjectCommand->BindInteger(1, row.exportable);
601 insertObjectCommand->BindInteger(2, static_cast<int>(row.dataType));
602 insertObjectCommand->BindInteger(3, static_cast<int>(row.algorithmType));
603 insertObjectCommand->BindInteger(4, row.encryptionScheme);
604 insertObjectCommand->BindBlob (5, row.iv);
605 insertObjectCommand->BindInteger(6, row.dataSize);
606 insertObjectCommand->BindBlob (7, row.data);
607 insertObjectCommand->BindBlob (8, row.tag);
609 // name table reference
610 insertObjectCommand->BindString (101, row.name.c_str());
611 insertObjectCommand->BindString (102, row.ownerLabel.c_str());
613 insertObjectCommand->Step();
617 #pragma GCC diagnostic pop