Enable -Wshadow and fix warnings
[platform/core/security/key-manager.git] / src / manager / service / db-crypto.cpp
1 /*
2  * Copyright (c) 2014-2020 Samsung Electronics Co., Ltd. All rights reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 /*
17  * @file        db-crypto.cpp
18  * @author      Zofia Abramowska (z.abramowska@samsung.com)
19  * @version     1.0
20  * @brief       Implementation of encrypted db access layer
21  */
22
23 #include <dlfcn.h>
24 #include <fcntl.h>
25 #include <fstream>
26 #include <libgen.h>
27 #include <sys/stat.h>
28 #include <db-crypto.h>
29 #include <dpl/db/sql_connection.h>
30 #include <dpl/errno_string.h>
31 #include <dpl/log/log.h>
32 #include <dpl/scoped_ptr.h>
33 #include <ckm/ckm-error.h>
34 #include <exception.h>
35 #include <utils.h>
36
37 #pragma GCC diagnostic push
38 #pragma GCC diagnostic warning "-Wdeprecated-declarations"
39
40 namespace {
41 const CKM::PermissionMask DEFAULT_PERMISSIONS =
42         static_cast<CKM::PermissionMask>(CKM::Permission::READ |
43                                                                          CKM::Permission::REMOVE);
44
45 const char *SCRIPTS_PATH = RO_DATA_DIR "/scripts/";
46
47 enum DBVersion : int {
48         DB_VERSION_1                   = 1,
49         DB_VERSION_2                   = 2,
50         /* ... since version 3, there is no need to manually
51          * recognize database version.
52          * Remember only that if doing changes to the database,
53          * increment and update DB_VERSION_CURRENT,
54          * then provide migration mechanism!
55          */
56         DB_VERSION_CURRENT             = 5
57 };
58
59 const char *SCRIPT_CREATE_SCHEMA                = "create_schema";
60 const char *SCRIPT_DROP_ALL_ITEMS               = "drop_all";
61 const char *SCRIPT_MIGRATE                      = "migrate_";
62
63 // common substitutions:
64 // 100 - idx
65 // 101 - name
66 // 102 - label
67 // 103 - value
68 // 104 - permissionLabel
69 // 105 - permissionMask
70 const char *DB_CMD_SCHEMA_SET =
71         "REPLACE INTO SCHEMA_INFO(name, value) "
72         "   VALUES(?101, ?103);";
73
74 const char *DB_CMD_SCHEMA_GET =
75         "SELECT * FROM SCHEMA_INFO WHERE name=?101;";
76
77 const char *DB_SCHEMA_VERSION_FIELD = "schema_version";
78
79
80 const char *DB_CMD_NAME_INSERT =
81         "INSERT INTO NAMES("
82         "   name, label) "
83         "   VALUES(?101, ?102);";
84
85 const char *DB_CMD_NAME_COUNT_ROWS =
86         "SELECT COUNT(idx) FROM NAMES WHERE name=?101 AND label=?102;";
87
88 const char *DB_CMD_NAME_DELETE =
89         "DELETE FROM NAMES WHERE name=?101 AND label=?102;";
90
91 const char *DB_CMD_NAME_DELETE_BY_OWNER =
92         "DELETE FROM NAMES WHERE label=?102;";
93
94
95 const char *DB_CMD_OBJECT_INSERT =
96         "INSERT INTO OBJECTS("
97         "   exportable, dataType,"
98         "   algorithmType, encryptionScheme,"
99         "   iv, dataSize, data, tag, idx, backendId) "
100         "   VALUES(?001, ?002, ?003, ?004, ?005, "
101         "          ?006, ?007, ?008,"
102         "          (SELECT idx FROM NAMES WHERE name=?101 and label=?102),"
103         "          ?009"
104         "         );";
105
106 const char *DB_CMD_OBJECT_UPDATE =
107         "UPDATE OR FAIL OBJECTS SET"
108         "   algorithmType = ?003,"
109         "   encryptionScheme = ?004,"
110         "   iv = ?005,"
111         "   dataSize = ?006,"
112         "   data = ?007,"
113         "   tag = ?008"
114         "   WHERE idx IN (SELECT idx FROM NAMES WHERE name=?101 and label=?102)"
115         "   AND dataType = ?002;";
116
117 const char *DB_CMD_OBJECT_SELECT_BY_NAME_AND_OWNER =
118         "SELECT * FROM [join_name_object_tables] "
119         " WHERE (dataType BETWEEN ?001 AND ?002) "
120         " AND name=?101 and label=?102;";
121
122
123 const char *DB_CMD_KEY_INSERT =
124         "INSERT INTO KEYS(label, key) VALUES (?, ?);";
125 const char *DB_CMD_KEY_SELECT =
126         "SELECT key FROM KEYS WHERE label=?;";
127 const char *DB_CMD_KEY_DELETE =
128         "DELETE FROM KEYS WHERE label=?";
129
130
131 const char *DB_CMD_PERMISSION_SET = // SQLite does not support updating views
132         "REPLACE INTO PERMISSIONS(permissionLabel, permissionMask, idx) "
133         " VALUES (?104, ?105, (SELECT idx FROM NAMES WHERE name=?101 and label=?102));";
134
135 const char *DB_CMD_PERMISSION_SELECT =
136         "SELECT permissionMask FROM [join_name_permission_tables] "
137         " WHERE permissionLabel=?104 "
138         " AND name=?101 and label=?102;";
139
140 const char *DB_CMD_PERMISSION_DELETE = // SQLite does not support updating views
141         "DELETE FROM PERMISSIONS WHERE permissionLabel=?104 AND "
142         " idx=(SELECT idx FROM NAMES WHERE name=?101 and label=?102);";
143
144
145 /*
146  * GROUP BY is necessary because of the following case:
147  * -There are several permissions to L1, N1 (label, name) from other accessors. When listing
148  *  objects accessible by L1 the query will produce one result (L1, N1) for each allowed
149  *  accessor but GROUP BY will reduce them to one so L1 will have (L1, N1) on its list only once
150  */
151 const char *DB_CMD_INFO_SELECT_BY_TYPE_AND_PERMISSION =
152         "SELECT label, name, dataType, backendId FROM [join_all_tables] "
153         " WHERE dataType>=?001 AND dataType<=?002 "
154         " AND permissionLabel=?104 AND permissionMask&?004!=0 GROUP BY idx;";
155 }
156
157 namespace CKM {
158 namespace DB {
159
160 namespace {
161 auto openSqlConnection(const std::string &path, const RawBuffer &rawPass)
162 {
163         auto conn = std::make_unique<SqlConnection>(path, SqlConnection::Flag::Option::CRW);
164         conn->SetKey(rawPass);
165         return conn;
166 }
167
168 void convertLegacyDatabase(const std::string &legacyPath, const std::string &path, const RawBuffer &rawPass)
169 {
170         {
171                 struct stat st;
172                 if (lstat(legacyPath.c_str(), &st)) {
173                         const auto err = errno;
174                         if (ENOENT != err)
175                                 ThrowErr(Exc::DatabaseFailed, "lstat failed: " << GetErrnoString(err));
176                         LogDebug("legacy db does not exist, proceeding");
177                         return;
178                 }
179                 if (!S_ISREG(st.st_mode))
180                         ThrowErr(Exc::DatabaseFailed, "legacy db not a regular file");
181         }
182         if (unlink(path.c_str()) && ENOENT != errno)
183                 ThrowErr(Exc::DatabaseFailed, "unlink failed: " << GetErrnoString());
184
185         // in no way to I condone the use of unique_ptr in this context; see: review
186         const auto handle = uptr<dlclose>(
187                         dlopen(LIB_INSTALL_DIR "/lib" DUMP_LEGACY_DB_LIBNAME ".so", RTLD_LAZY)
188                                 ?: ThrowErr(Exc::DatabaseFailed, "dlopen failed: " << dlerror()));
189         const auto dumpLegacyDb = (char *(*)(const char *, const unsigned char *, size_t))
190                 dlsym(handle.get(), "dumpLegacyDb")
191                         ?: ThrowErr(Exc::DatabaseFailed, "dlsym failed: " << dlerror());
192
193         {
194                 const RawBuffer pass = createHexPass(rawPass);
195                 const CharUniquePtr sqlDump(dumpLegacyDb(legacyPath.c_str(), pass.data(), pass.size())
196                                 ?: ThrowErr(Exc::DatabaseFailed, "dump failed"));
197
198                 // close to ensure full sync regardless of pragma synchronous value
199                 // EXTRA is needed to sync the dir after transaction but isn't used in ckm
200                 openSqlConnection(path, rawPass)->ExecCommand(sqlDump.get());
201                 LogDebug("legacy db converted, removing");
202         }
203
204         if (unlink(legacyPath.c_str()))
205                 ThrowErr(Exc::DatabaseFailed, "unlink legacy db failed: " << GetErrnoString());
206         std::string legacyPathCopy(legacyPath);
207         const auto legacyDirPath = dirname(&legacyPathCopy[0]);
208         const int legacyDirFd = TEMP_FAILURE_RETRY(open(legacyDirPath, O_RDONLY));
209         if (legacyDirFd < 0)
210                 ThrowErr(Exc::DatabaseFailed, "open failed: " << GetErrnoString());
211         const auto fsyncRes = fsync(legacyDirFd);
212         const auto fsyncErrno = errno;
213         close(legacyDirFd);
214         if (fsyncRes)
215                 ThrowErr(Exc::DatabaseFailed, "fsync failed: " << GetErrnoString(fsyncErrno));
216 }
217 } // namespace
218
219 Crypto::Crypto(const std::string &legacyPath, const std::string &path, const RawBuffer &rawPass) :
220         m_inUserTransaction(false)
221 {
222         try {
223                 convertLegacyDatabase(legacyPath, path, rawPass);
224                 m_connection = openSqlConnection(path, rawPass);
225                 initDatabase();
226                 m_connection->ExecCommand("VACUUM;");
227         } catch (const SqlConnection::Exception::ConnectionBroken &e) {
228                 ThrowErr(Exc::DatabaseFailed, "Couldn't connect to database: ", path,
229                                  e.GetMessage());
230         } catch (const SqlConnection::Exception::InvalidArguments &e) {
231                 ThrowErr(Exc::DatabaseFailed, "Couldn't set the key for database. ",
232                                  e.GetMessage());
233         } catch (const SqlConnection::Exception::SyntaxError &e) {
234                 ThrowErr(Exc::DatabaseFailed, "Couldn't initiate the database. ",
235                                  e.GetMessage());
236         } catch (const SqlConnection::Exception::InternalError &e) {
237                 ThrowErr(Exc::DatabaseFailed, "Couldn't create the database. ",
238                                  e.GetMessage());
239         }
240 }
241
242 Crypto::Crypto(Crypto &&other) :
243         m_connection(std::move(other.m_connection)),
244         m_inUserTransaction(other.m_inUserTransaction)
245 {
246         other.m_inUserTransaction = false;
247 }
248
249 Crypto &Crypto::operator=(Crypto &&other)
250 {
251         if (this == &other)
252                 return *this;
253
254         m_connection = std::move(other.m_connection);
255
256         m_inUserTransaction = other.m_inUserTransaction;
257         other.m_inUserTransaction = false;
258
259         return *this;
260 }
261
262 bool Crypto::getDBVersion(int &schemaVersion)
263 {
264         Transaction transaction(this);
265
266         if (m_connection->CheckTableExist("SCHEMA_INFO")) {
267                 SchemaInfo info(m_connection.get());
268                 if (info.getVersionInfo(schemaVersion)) {
269                         LogDebug("Current DB version: " << schemaVersion);
270                         return true;
271                 }
272         }
273
274         LogDebug("No DB version known or DB not present");
275
276         if (m_connection->CheckTableExist("CKM_TABLE")) {
277                 // special case: old CKM_TABLE exists
278                 schemaVersion = DB_VERSION_1;
279                 return true;
280         } else if (m_connection->CheckTableExist("NAME_TABLE")) {
281                 // special case: new scheme exists, but no SCHEMA_INFO table present
282                 schemaVersion = DB_VERSION_2;
283                 return true;
284         }
285
286         // not recognized - proceed with an empty DBs
287         return false;
288 }
289
290 void Crypto::initDatabase()
291 {
292         // run migration if old database is present
293         int schemaVersion;
294         if (!getDBVersion(schemaVersion) || schemaVersion > DB_VERSION_CURRENT) {
295                 LogDebug("no database or database corrupted, initializing the DB");
296                 goto fail;
297         }
298         if (schemaVersion < DB_VERSION_CURRENT) {
299                 // migration needed
300                 LogDebug("DB migration from version " << schemaVersion << " to version " <<
301                                  DB_VERSION_CURRENT << " started.");
302                 Transaction transaction(this);
303
304                 for (int vi = schemaVersion; vi < DB_VERSION_CURRENT; vi++) {
305                         ScriptOptional script = getMigrationScript(vi);
306
307                         if (!script) {
308                                 LogError("Error, script to migrate database from version: " << vi <<
309                                                  " to version: " << vi + 1 << " not available, resetting the DB");
310                                 goto fail;
311                         }
312
313                         LogInfo("migrating from version " << vi << " to version " << vi + 1);
314                         m_connection->ExecCommand(script->c_str());
315                 }
316
317                 // update DB version info
318                 SchemaInfo info(m_connection.get());
319                 info.setVersionInfo();
320                 transaction.commit();
321         }
322         return;
323
324 fail:
325         resetDB();
326 }
327
328 Crypto::ScriptOptional Crypto::getScript(const std::string &scriptName) const
329 {
330         std::string scriptPath = SCRIPTS_PATH + scriptName + std::string(".sql");
331         std::ifstream is(scriptPath);
332
333         if (is.fail()) {
334                 LogError("Script " << scriptPath << " not found!");
335                 return ScriptOptional();
336         }
337
338         std::istreambuf_iterator<char> begin(is), end;
339         return ScriptOptional(std::string(begin, end));
340 }
341
342 Crypto::ScriptOptional Crypto::getMigrationScript(int db_version) const
343 {
344         std::string scriptPath = std::string(SCRIPT_MIGRATE) + std::to_string(
345                                                                  db_version);
346         return getScript(scriptPath);
347 }
348
349 void Crypto::createDBSchema()
350 {
351         Transaction transaction(this);
352
353         ScriptOptional script = getScript(SCRIPT_CREATE_SCHEMA);
354
355         if (!script)
356                 ThrowErr(Exc::DatabaseFailed,
357                                  "Can not create the database schema: no initialization script");
358
359         m_connection->ExecCommand((*script).c_str());
360         SchemaInfo info(m_connection.get());
361         info.setVersionInfo();
362         transaction.commit();
363 }
364
365 void Crypto::resetDB()
366 {
367         Transaction transaction(this);
368         ScriptOptional script = getScript(SCRIPT_DROP_ALL_ITEMS);
369
370         if (!script)
371                 ThrowErr(Exc::DatabaseFailed, "Can not clear the database: no clearing script");
372
373         m_connection->ExecCommand((*script).c_str());
374         createDBSchema();
375         transaction.commit();
376 }
377
378 bool Crypto::isNameOwnerPresent(const Name &name, const ClientId &owner) const
379 {
380         try {
381                 NameTable nameTable(m_connection.get());
382                 return nameTable.isPresent(name, owner);
383         } catch (const SqlConnection::Exception::SyntaxError &) {
384                 LogError("Couldn't prepare insert statement");
385         } catch (const SqlConnection::Exception::InternalError &) {
386                 LogError("Couldn't execute insert statement");
387         }
388
389         ThrowErr(Exc::DatabaseFailed,
390                          "Couldn't check if name and owner pair is present");
391 }
392
393 void Crypto::saveRows(const Name &name, const ClientId &owner,
394                                           const RowVector &rows)
395 {
396         if (rows.empty())
397                 return;
398
399         try {
400                 // transaction is present in the layer above
401                 NameTable nameTable(m_connection.get());
402                 ObjectTable objectTable(m_connection.get());
403                 PermissionTable permissionTable(m_connection.get());
404                 nameTable.addRow(name, owner);
405
406                 for (const auto &i : rows)
407                         objectTable.addRow(i);
408
409                 permissionTable.setPermission(name,
410                                                                           owner,
411                                                                           owner,
412                                                                           static_cast<int>(DEFAULT_PERMISSIONS));
413                 return;
414         } catch (const SqlConnection::Exception::SyntaxError &e) {
415                 LogError("Couldn't prepare insert statement: " <<
416                                  e.GetMessage());
417         } catch (const SqlConnection::Exception::InternalError &e) {
418                 LogError("Couldn't execute insert statement: " <<
419                                  e.GetMessage());
420         }
421
422         ThrowErr(Exc::DatabaseFailed, "Couldn't save Row");
423 }
424
425 void Crypto::saveRow(const Row &row)
426 {
427         try {
428                 // transaction is present in the layer above
429                 NameTable nameTable(m_connection.get());
430                 ObjectTable objectTable(m_connection.get());
431                 PermissionTable permissionTable(m_connection.get());
432                 nameTable.addRow(row.name, row.owner);
433                 objectTable.addRow(row);
434                 permissionTable.setPermission(row.name,
435                                                                           row.owner,
436                                                                           row.owner,
437                                                                           static_cast<int>(DEFAULT_PERMISSIONS));
438                 return;
439         } catch (const SqlConnection::Exception::SyntaxError &) {
440                 LogError("Couldn't prepare insert statement");
441         } catch (const SqlConnection::Exception::InternalError &) {
442                 LogError("Couldn't execute insert statement");
443         }
444
445         ThrowErr(Exc::DatabaseFailed, "Couldn't save Row");
446 }
447
448 void Crypto::updateRow(const Row &row)
449 {
450         try {
451                 // transaction is present in the layer above
452                 ObjectTable objectTable(m_connection.get());
453                 objectTable.updateRow(row);
454                 return;
455         } catch (const SqlConnection::Exception::SyntaxError &) {
456                 LogError("Couldn't prepare update statement");
457         } catch (const SqlConnection::Exception::InternalError &) {
458                 LogError("Couldn't execute update statement");
459         }
460
461         ThrowErr(Exc::DatabaseFailed, "Couldn't update Row");
462 }
463
464 bool Crypto::deleteRow(
465         const Name &name,
466         const ClientId &owner)
467 {
468         try {
469                 // transaction is present in the layer above
470                 NameTable nameTable(m_connection.get());
471
472                 if (nameTable.isPresent(name, owner)) {
473                         nameTable.deleteRow(name, owner);
474                         return true;
475                 }
476
477                 return false;
478         } catch (const SqlConnection::Exception::SyntaxError &) {
479                 LogError("Couldn't prepare delete statement");
480         } catch (const SqlConnection::Exception::InternalError &) {
481                 LogError("Couldn't execute delete statement");
482         }
483
484         ThrowErr(Exc::DatabaseFailed,
485                          "Couldn't delete Row for name ", name, " using owner id ", owner);
486 }
487
488 Row Crypto::getRow(
489         const SqlConnection::DataCommandUniquePtr &selectCommand) const
490 {
491         Row row;
492         row.name = selectCommand->GetColumnString(0);
493         row.owner = selectCommand->GetColumnString(1);
494         row.exportable = selectCommand->GetColumnInteger(2);
495         row.dataType = static_cast<DataType::Type>(selectCommand->GetColumnInteger(3));
496         row.algorithmType =
497                 static_cast<DBCMAlgType>(selectCommand->GetColumnInteger(4));
498         row.encryptionScheme = selectCommand->GetColumnInteger(5);
499         row.iv = selectCommand->GetColumnBlob(6);
500         row.dataSize = selectCommand->GetColumnInteger(7);
501         row.data = selectCommand->GetColumnBlob(8);
502         row.tag = selectCommand->GetColumnBlob(9);
503         row.backendId = static_cast<CryptoBackend>(selectCommand->GetColumnInteger(11));
504         return row;
505 }
506
507 PermissionMaskOptional Crypto::getPermissionRow(
508         const Name &name,
509         const ClientId &owner,
510         const ClientId &accessor) const
511 {
512         try {
513                 PermissionTable permissionTable(m_connection.get());
514                 return permissionTable.getPermissionRow(name, owner, accessor);
515         } catch (const SqlConnection::Exception::InvalidColumn &) {
516                 LogError("Select statement invalid column error");
517         } catch (const SqlConnection::Exception::SyntaxError &) {
518                 LogError("Couldn't prepare select statement");
519         } catch (const SqlConnection::Exception::InternalError &) {
520                 LogError("Couldn't execute select statement");
521         }
522
523         return PermissionMaskOptional();
524 }
525
526 Crypto::RowOptional Crypto::getRow(
527         const Name &name,
528         const ClientId &owner,
529         DataType type)
530 {
531         return getRow(name, owner, type, type);
532 }
533
534 Crypto::RowOptional Crypto::getRow(
535         const Name &name,
536         const ClientId &owner,
537         DataType typeRangeStart,
538         DataType typeRangeStop)
539 {
540         try {
541                 SqlConnection::DataCommandUniquePtr selectCommand =
542                         m_connection->PrepareDataCommand(DB_CMD_OBJECT_SELECT_BY_NAME_AND_OWNER);
543                 selectCommand->BindInteger(1, typeRangeStart);
544                 selectCommand->BindInteger(2, typeRangeStop);
545
546                 // name table reference
547                 selectCommand->BindString(101, name.c_str());
548                 selectCommand->BindString(102, owner.c_str());
549
550                 if (selectCommand->Step()) {
551                         // extract data
552                         Row current_row = getRow(selectCommand);
553
554                         // all okay, proceed
555                         return RowOptional(current_row);
556                 } else {
557                         return RowOptional();
558                 }
559         } catch (const SqlConnection::Exception::InvalidColumn &) {
560                 LogError("Select statement invalid column error");
561         } catch (const SqlConnection::Exception::SyntaxError &) {
562                 LogError("Couldn't prepare select statement");
563         } catch (const SqlConnection::Exception::InternalError &) {
564                 LogError("Couldn't execute select statement");
565         }
566
567         ThrowErr(Exc::DatabaseFailed,
568                          "Couldn't get row of type <",
569                          static_cast<int>(typeRangeStart), ",",
570                          static_cast<int>(typeRangeStop), ">",
571                          " name ", name, " with owner ", owner);
572 }
573
574 void Crypto::getRows(
575         const Name &name,
576         const ClientId &owner,
577         DataType type,
578         RowVector &output)
579 {
580         getRows(name, owner, type, type, output);
581 }
582
583 void Crypto::getRows(
584         const Name &name,
585         const ClientId &owner,
586         DataType typeRangeStart,
587         DataType typeRangeStop,
588         RowVector &output)
589 {
590         try {
591                 SqlConnection::DataCommandUniquePtr selectCommand =
592                         m_connection->PrepareDataCommand(DB_CMD_OBJECT_SELECT_BY_NAME_AND_OWNER);
593                 selectCommand->BindInteger(1, typeRangeStart);
594                 selectCommand->BindInteger(2, typeRangeStop);
595
596                 // name table reference
597                 selectCommand->BindString(101, name.c_str());
598                 selectCommand->BindString(102, owner.c_str());
599
600                 while (selectCommand->Step()) {
601                         // extract data
602                         output.push_back(getRow(selectCommand));
603                 }
604
605                 return;
606         } catch (const SqlConnection::Exception::InvalidColumn &) {
607                 LogError("Select statement invalid column error");
608         } catch (const SqlConnection::Exception::SyntaxError &) {
609                 LogError("Couldn't prepare select statement");
610         } catch (const SqlConnection::Exception::InternalError &) {
611                 LogError("Couldn't execute select statement");
612         }
613
614         ThrowErr(Exc::DatabaseFailed,
615                          "Couldn't get row of type <",
616                          static_cast<int>(typeRangeStart), ",",
617                          static_cast<int>(typeRangeStop), ">",
618                          " name ", name, " with owner label ", owner);
619 }
620
621 void Crypto::listInfos(const ClientId &accessor,
622                                            AliasInfoVector &aliasInfoVector,
623                                            DataType type)
624 {
625         listInfos(accessor, aliasInfoVector, type, type);
626 }
627
628 void Crypto::listInfos(const ClientId &accessor,
629                                            AliasInfoVector &aliasInfoVector,
630                                            DataType typeRangeStart,
631                                            DataType typeRangeStop)
632 {
633         try {
634                 Transaction transaction(this);
635                 SqlConnection::DataCommandUniquePtr selectCommand =
636                         m_connection->PrepareDataCommand(DB_CMD_INFO_SELECT_BY_TYPE_AND_PERMISSION);
637                 selectCommand->BindInteger(1, typeRangeStart);
638                 selectCommand->BindInteger(2, typeRangeStop);
639                 selectCommand->BindString(104, accessor.c_str());
640                 selectCommand->BindInteger(4, static_cast<int>(Permission::READ | Permission::REMOVE));
641
642                 while (selectCommand->Step()) {
643                         ClientId owner = selectCommand->GetColumnString(0);
644                         Name name = selectCommand->GetColumnString(1);
645                         int type = selectCommand->GetColumnInteger(2);
646                         auto cryptoBackend = static_cast<CryptoBackend>(selectCommand->GetColumnInteger(3));
647                         BackendId backendId;
648                         switch (cryptoBackend) {
649                         case CryptoBackend::OpenSSL:
650                                 backendId = BackendId::SW;
651                                 break;
652                         case CryptoBackend::TrustZone:
653                                 backendId = BackendId::TZ;
654                                 break;
655                         default:
656                                 ThrowErr(Exc::DatabaseFailed,
657                                          "Unexpected backend: ",
658                                          static_cast<unsigned>(cryptoBackend));
659                         }
660                         aliasInfoVector.emplace_back(AliasSupport::merge(owner, name), type, backendId);
661                 }
662
663                 return;
664         } catch (const SqlConnection::Exception::InvalidColumn &) {
665                 LogError("Select statement invalid column error");
666         } catch (const SqlConnection::Exception::SyntaxError &) {
667                 LogError("Couldn't prepare select statement");
668         } catch (const SqlConnection::Exception::InternalError &) {
669                 LogError("Couldn't execute select statement");
670         }
671
672         ThrowErr(Exc::DatabaseFailed,
673                          "Couldn't list names of type <",
674                          typeRangeStart, ",",
675                          typeRangeStop, ">",
676                          " accessible to client ", accessor);
677 }
678
679 void Crypto::saveKey(
680         const ClientId &owner,
681         const RawBuffer &key)
682 {
683         try {
684                 SqlConnection::DataCommandUniquePtr insertCommand =
685                         m_connection->PrepareDataCommand(DB_CMD_KEY_INSERT);
686                 insertCommand->BindString(1, owner.c_str());
687                 insertCommand->BindBlob(2, key);
688                 insertCommand->Step();
689                 return;
690         } catch (const SqlConnection::Exception::SyntaxError &) {
691                 LogError("Couldn't prepare insert key statement");
692         } catch (const SqlConnection::Exception::InternalError &) {
693                 LogError("Couldn't execute insert statement");
694         }
695
696         ThrowErr(Exc::DatabaseFailed, "Couldn't save key for owner ", owner);
697 }
698
699 Crypto::RawBufferOptional Crypto::getKey(const ClientId &owner)
700 {
701         try {
702                 SqlConnection::DataCommandUniquePtr selectCommand =
703                         m_connection->PrepareDataCommand(DB_CMD_KEY_SELECT);
704                 selectCommand->BindString(1, owner.c_str());
705
706                 if (selectCommand->Step())
707                         return RawBufferOptional(selectCommand->GetColumnBlob(0));
708                 else
709                         return RawBufferOptional();
710         } catch (const SqlConnection::Exception::InvalidColumn &) {
711                 LogError("Select statement invalid column error");
712         } catch (const SqlConnection::Exception::SyntaxError &) {
713                 LogError("Couldn't prepare insert key statement");
714         } catch (const SqlConnection::Exception::InternalError &) {
715                 LogError("Couldn't execute insert statement");
716         }
717
718         ThrowErr(Exc::DatabaseFailed, "Couldn't get key for owner ", owner);
719 }
720
721 void Crypto::deleteKey(const ClientId &owner)
722 {
723         try {
724                 Transaction transaction(this);
725
726                 SqlConnection::DataCommandUniquePtr deleteCommand =
727                         m_connection->PrepareDataCommand(DB_CMD_KEY_DELETE);
728                 deleteCommand->BindString(1, owner.c_str());
729                 deleteCommand->Step();
730
731                 NameTable nameTable(m_connection.get());
732                 nameTable.deleteAllRows(owner);
733
734                 transaction.commit();
735                 return;
736         } catch (const SqlConnection::Exception::SyntaxError &) {
737                 LogError("Couldn't prepare insert key statement");
738         } catch (const SqlConnection::Exception::InternalError &) {
739                 LogError("Couldn't execute insert statement");
740         }
741
742         ThrowErr(Exc::DatabaseFailed, "Couldn't delete key for owner ", owner);
743 }
744
745 void Crypto::setPermission(
746         const Name &name,
747         const ClientId &owner,
748         const ClientId &accessor,
749         const PermissionMask permissionMask)
750 {
751         try {
752                 PermissionTable permissionTable(m_connection.get());
753                 permissionTable.setPermission(name, owner, accessor, permissionMask);
754                 return;
755         } catch (const SqlConnection::Exception::SyntaxError &) {
756                 LogError("Couldn't prepare set statement");
757         } catch (const SqlConnection::Exception::InternalError &) {
758                 LogError("Couldn't execute set statement");
759         }
760
761         ThrowErr(Exc::DatabaseFailed, "Couldn't set permissions for name ", name);
762 }
763
764 void Crypto::SchemaInfo::setVersionInfo()
765 {
766         SqlConnection::DataCommandUniquePtr insertContextCommand =
767                 m_connection->PrepareDataCommand(DB_CMD_SCHEMA_SET);
768         insertContextCommand->BindString(101, DB_SCHEMA_VERSION_FIELD);
769         insertContextCommand->BindString(103,
770                                                                          std::to_string(DB_VERSION_CURRENT).c_str());
771         insertContextCommand->Step();
772 }
773
774 bool Crypto::SchemaInfo::getVersionInfo(int &version)
775 {
776         SqlConnection::DataCommandUniquePtr selectCommand =
777                 m_connection->PrepareDataCommand(DB_CMD_SCHEMA_GET);
778         selectCommand->BindString(101, DB_SCHEMA_VERSION_FIELD);
779
780         if (selectCommand->Step()) {
781                 version = static_cast<int>(atoi(selectCommand->GetColumnString(1).c_str()));
782                 return true;
783         }
784         return false;
785 }
786
787 void Crypto::PermissionTable::setPermission(
788         const Name &name,
789         const ClientId &owner,
790         const ClientId &accessor,
791         const PermissionMask permissionMask)
792 {
793         if (permissionMask == Permission::NONE) {
794                 // clear permissions
795                 SqlConnection::DataCommandUniquePtr deletePermissionCommand =
796                         m_connection->PrepareDataCommand(DB_CMD_PERMISSION_DELETE);
797                 deletePermissionCommand->BindString(104, accessor.c_str());
798                 deletePermissionCommand->BindString(101, name.c_str());
799                 deletePermissionCommand->BindString(102, owner.c_str());
800                 deletePermissionCommand->Step();
801         } else {
802                 // add new permissions
803                 SqlConnection::DataCommandUniquePtr setPermissionCommand =
804                         m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SET);
805                 setPermissionCommand->BindString(104, accessor.c_str());
806                 setPermissionCommand->BindInteger(105, static_cast<int>(permissionMask));
807                 setPermissionCommand->BindString(101, name.c_str());
808                 setPermissionCommand->BindString(102, owner.c_str());
809                 setPermissionCommand->Step();
810         }
811 }
812
813 PermissionMaskOptional Crypto::PermissionTable::getPermissionRow(
814         const Name &name,
815         const ClientId &owner,
816         const ClientId &accessor) const
817 {
818         SqlConnection::DataCommandUniquePtr selectCommand =
819                 m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SELECT);
820         selectCommand->BindString(104, accessor.c_str());
821
822         // name table reference
823         selectCommand->BindString(101, name.c_str());
824         selectCommand->BindString(102, owner.c_str());
825
826         if (selectCommand->Step()) {
827                 // there is entry for the <name, owner> pair
828                 return PermissionMaskOptional(PermissionMask(selectCommand->GetColumnInteger(
829                                                                                   0)));
830         }
831
832         return PermissionMaskOptional();
833 }
834
835 void Crypto::NameTable::addRow(
836         const Name &name,
837         const ClientId &owner)
838 {
839         // insert NAMES item
840         SqlConnection::DataCommandUniquePtr insertNameCommand =
841                 m_connection->PrepareDataCommand(DB_CMD_NAME_INSERT);
842         insertNameCommand->BindString(101, name.c_str());
843         insertNameCommand->BindString(102, owner.c_str());
844         insertNameCommand->Step();
845 }
846
847 void Crypto::NameTable::deleteRow(
848         const Name &name,
849         const ClientId &ownerOwner)
850 {
851         SqlConnection::DataCommandUniquePtr deleteCommand =
852                 m_connection->PrepareDataCommand(DB_CMD_NAME_DELETE);
853         deleteCommand->BindString(101, name.c_str());
854         deleteCommand->BindString(102, ownerOwner.c_str());
855
856         // Step() result code does not provide information whether
857         // anything was removed.
858         deleteCommand->Step();
859 }
860
861 void Crypto::NameTable::deleteAllRows(const ClientId &owner)
862 {
863         SqlConnection::DataCommandUniquePtr deleteData =
864                 m_connection->PrepareDataCommand(DB_CMD_NAME_DELETE_BY_OWNER);
865         deleteData->BindString(102, owner.c_str());
866
867         // Step() result code does not provide information whether
868         // anything was removed.
869         deleteData->Step();
870 }
871
872 bool Crypto::NameTable::isPresent(const Name &name,
873                                                                   const ClientId &owner) const
874 {
875         SqlConnection::DataCommandUniquePtr checkCmd =
876                 m_connection->PrepareDataCommand(DB_CMD_NAME_COUNT_ROWS);
877         checkCmd->BindString(101, name.c_str());
878         checkCmd->BindString(102, owner.c_str());
879
880         if (checkCmd->Step()) {
881                 int element_count = checkCmd->GetColumnInteger(0);
882                 LogDebug("Item name: " << name  << " owner: " << owner <<
883                                  " hit count: " << element_count);
884
885                 if (element_count > 0)
886                         return true;
887         }
888
889         return false;
890 }
891
892 void Crypto::ObjectTable::addRow(const Row &row)
893 {
894         SqlConnection::DataCommandUniquePtr insertObjectCommand =
895                 m_connection->PrepareDataCommand(DB_CMD_OBJECT_INSERT);
896         insertObjectCommand->BindInteger(1, row.exportable);
897         insertObjectCommand->BindInteger(2, row.dataType);
898         insertObjectCommand->BindInteger(3, static_cast<int>(row.algorithmType));
899         insertObjectCommand->BindInteger(4, row.encryptionScheme);
900         insertObjectCommand->BindBlob(5, row.iv);
901         insertObjectCommand->BindInteger(6, row.dataSize);
902         insertObjectCommand->BindBlob(7, row.data);
903         insertObjectCommand->BindBlob(8, row.tag);
904         insertObjectCommand->BindInteger(9, static_cast<int>(row.backendId));
905
906         // name table reference
907         insertObjectCommand->BindString(101, row.name.c_str());
908         insertObjectCommand->BindString(102, row.owner.c_str());
909
910         insertObjectCommand->Step();
911 }
912
913 void Crypto::ObjectTable::updateRow(const Row &row)
914 {
915         SqlConnection::DataCommandUniquePtr updateObjectCommand =
916                 m_connection->PrepareDataCommand(DB_CMD_OBJECT_UPDATE);
917         updateObjectCommand->BindInteger(2, row.dataType);
918         updateObjectCommand->BindInteger(3, static_cast<int>(row.algorithmType));
919         updateObjectCommand->BindInteger(4, row.encryptionScheme);
920         updateObjectCommand->BindBlob(5, row.iv);
921         updateObjectCommand->BindInteger(6, row.dataSize);
922         updateObjectCommand->BindBlob(7, row.data);
923         updateObjectCommand->BindBlob(8, row.tag);
924
925         // name table reference
926         updateObjectCommand->BindString(101, row.name.c_str());
927         updateObjectCommand->BindString(102, row.owner.c_str());
928
929         updateObjectCommand->Step();
930 }
931 } // namespace DB
932 } // namespace CKM
933
934 #pragma GCC diagnostic pop