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