ceccd6983fe247c3990142c634ca1055ecc98687
[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 SchemaInfo(m_connection.get());
268                 if (SchemaInfo.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 SchemaInfo(m_connection.get());
319                 SchemaInfo.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 SchemaInfo(m_connection.get());
361         SchemaInfo.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 &owner,
622                                            AliasInfoVector &aliasInfoVector,
623                                            DataType type)
624 {
625         listInfos(owner, aliasInfoVector, type, type);
626 }
627
628 void Crypto::listInfos(const ClientId &owner,
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, owner.c_str());
640                 selectCommand->BindInteger(4,
641                                                                    static_cast<int>(Permission::READ | Permission::REMOVE));
642
643                 while (selectCommand->Step()) {
644                         ClientId owner = selectCommand->GetColumnString(0);
645                         Name name = selectCommand->GetColumnString(1);
646                         int type = selectCommand->GetColumnInteger(2);
647                         auto cryptoBackend = static_cast<CryptoBackend>(selectCommand->GetColumnInteger(3));
648                         BackendId backendId;
649                         switch (cryptoBackend) {
650                         case CryptoBackend::OpenSSL:
651                                 backendId = BackendId::SW;
652                                 break;
653                         case CryptoBackend::TrustZone:
654                                 backendId = BackendId::TZ;
655                                 break;
656                         default:
657                                 ThrowErr(Exc::DatabaseFailed,
658                                          "Unexpected backend: ",
659                                          static_cast<unsigned>(cryptoBackend));
660                         }
661                         aliasInfoVector.emplace_back(AliasSupport::merge(owner, name), type, backendId);
662                 }
663
664                 return;
665         } catch (const SqlConnection::Exception::InvalidColumn &) {
666                 LogError("Select statement invalid column error");
667         } catch (const SqlConnection::Exception::SyntaxError &) {
668                 LogError("Couldn't prepare select statement");
669         } catch (const SqlConnection::Exception::InternalError &) {
670                 LogError("Couldn't execute select statement");
671         }
672
673         ThrowErr(Exc::DatabaseFailed,
674                          "Couldn't list names of type <",
675                          typeRangeStart, ",",
676                          typeRangeStop, ">",
677                          " accessible to client ", owner);
678 }
679
680 void Crypto::saveKey(
681         const ClientId &owner,
682         const RawBuffer &key)
683 {
684         try {
685                 SqlConnection::DataCommandUniquePtr insertCommand =
686                         m_connection->PrepareDataCommand(DB_CMD_KEY_INSERT);
687                 insertCommand->BindString(1, owner.c_str());
688                 insertCommand->BindBlob(2, key);
689                 insertCommand->Step();
690                 return;
691         } catch (const SqlConnection::Exception::SyntaxError &) {
692                 LogError("Couldn't prepare insert key statement");
693         } catch (const SqlConnection::Exception::InternalError &) {
694                 LogError("Couldn't execute insert statement");
695         }
696
697         ThrowErr(Exc::DatabaseFailed, "Couldn't save key for owner ", owner);
698 }
699
700 Crypto::RawBufferOptional Crypto::getKey(const ClientId &owner)
701 {
702         try {
703                 SqlConnection::DataCommandUniquePtr selectCommand =
704                         m_connection->PrepareDataCommand(DB_CMD_KEY_SELECT);
705                 selectCommand->BindString(1, owner.c_str());
706
707                 if (selectCommand->Step())
708                         return RawBufferOptional(selectCommand->GetColumnBlob(0));
709                 else
710                         return RawBufferOptional();
711         } catch (const SqlConnection::Exception::InvalidColumn &) {
712                 LogError("Select statement invalid column error");
713         } catch (const SqlConnection::Exception::SyntaxError &) {
714                 LogError("Couldn't prepare insert key statement");
715         } catch (const SqlConnection::Exception::InternalError &) {
716                 LogError("Couldn't execute insert statement");
717         }
718
719         ThrowErr(Exc::DatabaseFailed, "Couldn't get key for owner ", owner);
720 }
721
722 void Crypto::deleteKey(const ClientId &owner)
723 {
724         try {
725                 Transaction transaction(this);
726
727                 SqlConnection::DataCommandUniquePtr deleteCommand =
728                         m_connection->PrepareDataCommand(DB_CMD_KEY_DELETE);
729                 deleteCommand->BindString(1, owner.c_str());
730                 deleteCommand->Step();
731
732                 NameTable nameTable(m_connection.get());
733                 nameTable.deleteAllRows(owner);
734
735                 transaction.commit();
736                 return;
737         } catch (const SqlConnection::Exception::SyntaxError &) {
738                 LogError("Couldn't prepare insert key statement");
739         } catch (const SqlConnection::Exception::InternalError &) {
740                 LogError("Couldn't execute insert statement");
741         }
742
743         ThrowErr(Exc::DatabaseFailed, "Couldn't delete key for owner ", owner);
744 }
745
746 void Crypto::setPermission(
747         const Name &name,
748         const ClientId &owner,
749         const ClientId &accessor,
750         const PermissionMask permissionMask)
751 {
752         try {
753                 PermissionTable permissionTable(m_connection.get());
754                 permissionTable.setPermission(name, owner, accessor, permissionMask);
755                 return;
756         } catch (const SqlConnection::Exception::SyntaxError &) {
757                 LogError("Couldn't prepare set statement");
758         } catch (const SqlConnection::Exception::InternalError &) {
759                 LogError("Couldn't execute set statement");
760         }
761
762         ThrowErr(Exc::DatabaseFailed, "Couldn't set permissions for name ", name);
763 }
764
765 void Crypto::SchemaInfo::setVersionInfo()
766 {
767         SqlConnection::DataCommandUniquePtr insertContextCommand =
768                 m_connection->PrepareDataCommand(DB_CMD_SCHEMA_SET);
769         insertContextCommand->BindString(101, DB_SCHEMA_VERSION_FIELD);
770         insertContextCommand->BindString(103,
771                                                                          std::to_string(DB_VERSION_CURRENT).c_str());
772         insertContextCommand->Step();
773 }
774
775 bool Crypto::SchemaInfo::getVersionInfo(int &version)
776 {
777         SqlConnection::DataCommandUniquePtr selectCommand =
778                 m_connection->PrepareDataCommand(DB_CMD_SCHEMA_GET);
779         selectCommand->BindString(101, DB_SCHEMA_VERSION_FIELD);
780
781         if (selectCommand->Step()) {
782                 version = static_cast<int>(atoi(selectCommand->GetColumnString(1).c_str()));
783                 return true;
784         }
785         return false;
786 }
787
788 void Crypto::PermissionTable::setPermission(
789         const Name &name,
790         const ClientId &owner,
791         const ClientId &accessor,
792         const PermissionMask permissionMask)
793 {
794         if (permissionMask == Permission::NONE) {
795                 // clear permissions
796                 SqlConnection::DataCommandUniquePtr deletePermissionCommand =
797                         m_connection->PrepareDataCommand(DB_CMD_PERMISSION_DELETE);
798                 deletePermissionCommand->BindString(104, accessor.c_str());
799                 deletePermissionCommand->BindString(101, name.c_str());
800                 deletePermissionCommand->BindString(102, owner.c_str());
801                 deletePermissionCommand->Step();
802         } else {
803                 // add new permissions
804                 SqlConnection::DataCommandUniquePtr setPermissionCommand =
805                         m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SET);
806                 setPermissionCommand->BindString(104, accessor.c_str());
807                 setPermissionCommand->BindInteger(105, static_cast<int>(permissionMask));
808                 setPermissionCommand->BindString(101, name.c_str());
809                 setPermissionCommand->BindString(102, owner.c_str());
810                 setPermissionCommand->Step();
811         }
812 }
813
814 PermissionMaskOptional Crypto::PermissionTable::getPermissionRow(
815         const Name &name,
816         const ClientId &owner,
817         const ClientId &accessor) const
818 {
819         SqlConnection::DataCommandUniquePtr selectCommand =
820                 m_connection->PrepareDataCommand(DB_CMD_PERMISSION_SELECT);
821         selectCommand->BindString(104, accessor.c_str());
822
823         // name table reference
824         selectCommand->BindString(101, name.c_str());
825         selectCommand->BindString(102, owner.c_str());
826
827         if (selectCommand->Step()) {
828                 // there is entry for the <name, owner> pair
829                 return PermissionMaskOptional(PermissionMask(selectCommand->GetColumnInteger(
830                                                                                   0)));
831         }
832
833         return PermissionMaskOptional();
834 }
835
836 void Crypto::NameTable::addRow(
837         const Name &name,
838         const ClientId &owner)
839 {
840         // insert NAMES item
841         SqlConnection::DataCommandUniquePtr insertNameCommand =
842                 m_connection->PrepareDataCommand(DB_CMD_NAME_INSERT);
843         insertNameCommand->BindString(101, name.c_str());
844         insertNameCommand->BindString(102, owner.c_str());
845         insertNameCommand->Step();
846 }
847
848 void Crypto::NameTable::deleteRow(
849         const Name &name,
850         const ClientId &ownerOwner)
851 {
852         SqlConnection::DataCommandUniquePtr deleteCommand =
853                 m_connection->PrepareDataCommand(DB_CMD_NAME_DELETE);
854         deleteCommand->BindString(101, name.c_str());
855         deleteCommand->BindString(102, ownerOwner.c_str());
856
857         // Step() result code does not provide information whether
858         // anything was removed.
859         deleteCommand->Step();
860 }
861
862 void Crypto::NameTable::deleteAllRows(const ClientId &owner)
863 {
864         SqlConnection::DataCommandUniquePtr deleteData =
865                 m_connection->PrepareDataCommand(DB_CMD_NAME_DELETE_BY_OWNER);
866         deleteData->BindString(102, owner.c_str());
867
868         // Step() result code does not provide information whether
869         // anything was removed.
870         deleteData->Step();
871 }
872
873 bool Crypto::NameTable::isPresent(const Name &name,
874                                                                   const ClientId &owner) const
875 {
876         SqlConnection::DataCommandUniquePtr checkCmd =
877                 m_connection->PrepareDataCommand(DB_CMD_NAME_COUNT_ROWS);
878         checkCmd->BindString(101, name.c_str());
879         checkCmd->BindString(102, owner.c_str());
880
881         if (checkCmd->Step()) {
882                 int element_count = checkCmd->GetColumnInteger(0);
883                 LogDebug("Item name: " << name  << " owner: " << owner <<
884                                  " hit count: " << element_count);
885
886                 if (element_count > 0)
887                         return true;
888         }
889
890         return false;
891 }
892
893 void Crypto::ObjectTable::addRow(const Row &row)
894 {
895         SqlConnection::DataCommandUniquePtr insertObjectCommand =
896                 m_connection->PrepareDataCommand(DB_CMD_OBJECT_INSERT);
897         insertObjectCommand->BindInteger(1, row.exportable);
898         insertObjectCommand->BindInteger(2, row.dataType);
899         insertObjectCommand->BindInteger(3, static_cast<int>(row.algorithmType));
900         insertObjectCommand->BindInteger(4, row.encryptionScheme);
901         insertObjectCommand->BindBlob(5, row.iv);
902         insertObjectCommand->BindInteger(6, row.dataSize);
903         insertObjectCommand->BindBlob(7, row.data);
904         insertObjectCommand->BindBlob(8, row.tag);
905         insertObjectCommand->BindInteger(9, static_cast<int>(row.backendId));
906
907         // name table reference
908         insertObjectCommand->BindString(101, row.name.c_str());
909         insertObjectCommand->BindString(102, row.owner.c_str());
910
911         insertObjectCommand->Step();
912 }
913
914 void Crypto::ObjectTable::updateRow(const Row &row)
915 {
916         SqlConnection::DataCommandUniquePtr updateObjectCommand =
917                 m_connection->PrepareDataCommand(DB_CMD_OBJECT_UPDATE);
918         updateObjectCommand->BindInteger(2, row.dataType);
919         updateObjectCommand->BindInteger(3, static_cast<int>(row.algorithmType));
920         updateObjectCommand->BindInteger(4, row.encryptionScheme);
921         updateObjectCommand->BindBlob(5, row.iv);
922         updateObjectCommand->BindInteger(6, row.dataSize);
923         updateObjectCommand->BindBlob(7, row.data);
924         updateObjectCommand->BindBlob(8, row.tag);
925
926         // name table reference
927         updateObjectCommand->BindString(101, row.name.c_str());
928         updateObjectCommand->BindString(102, row.owner.c_str());
929
930         updateObjectCommand->Step();
931 }
932 } // namespace DB
933 } // namespace CKM
934
935 #pragma GCC diagnostic pop