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