Transactions
[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 <db-crypto.h>
24 #include <dpl/db/sql_connection.h>
25 #include <dpl/log/log.h>
26 #include <ckm/ckm-error.h>
27
28 #pragma GCC diagnostic push
29 #pragma GCC diagnostic warning "-Wdeprecated-declarations"
30
31 namespace {
32     const char *main_table = "CKM_TABLE";
33     const char *key_table = "KEY_TABLE";
34
35 // CKM_TABLE (alias TEXT, label TEXT, restricted INT, exportable INT, dataType INT,
36 //            algorithmType INT, encryptionScheme INT, iv BLOB, dataSize INT, data BLOB)
37
38     const char *db_create_main_cmd =
39             "CREATE TABLE CKM_TABLE("
40             "   alias TEXT NOT NULL,"
41             "   label TEXT NOT NULL,"
42             "   restricted INTEGER NOT NULL,"
43             "   exportable INTEGER NOT NULL,"
44             "   dataType INTEGER NOT NULL,"
45             "   algorithmType INTEGER NOT NULL,"
46             "   encryptionScheme INTEGER NOT NULL,"
47             "   iv BLOB NOT NULL,"
48             "   dataSize INTEGER NOT NULL,"
49             "   data BLOB NOT NULL,"
50             "   PRIMARY KEY(alias, label),"
51             "   UNIQUE(alias, restricted)"
52             ");";
53
54
55     const char *insert_main_cmd =
56             "INSERT INTO CKM_TABLE("
57             //      1   2       3           4
58             "   alias, label, restricted, exportable,"
59             //      5           6           7
60             "   dataType, algorithmType, encryptionScheme,"
61             //  8       9       10
62             "   iv, dataSize, data) "
63             "VALUES("
64             "   ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
65
66     const char *select_alias_cmd =
67             //                                   1              2                            3
68             "SELECT * FROM CKM_TABLE WHERE alias=? AND dataType=? AND restricted=1 AND label=? "
69             " UNION ALL "
70             //                                    4              5
71             " SELECT * FROM CKM_TABLE WHERE alias=? AND dataType=?  AND restricted=0;";
72
73     const char *select_check_alias_cmd =
74             //                                          1           2
75             "SELECT dataType FROM CKM_TABLE WHERE alias=? AND label=? AND restricted=1; ";
76
77     const char *select_check_global_alias_cmd =
78             //                                       1
79             "SELECT label FROM CKM_TABLE WHERE alias=? AND restricted=0;";
80
81     const char *select_key_alias_cmd =
82             //                                   1
83             "SELECT * FROM CKM_TABLE WHERE alias=?"
84             //                     2     3
85             " AND dataType BETWEEN ? AND ? "
86             //                           4
87             " AND (restricted=0 OR label=?);";
88
89     const char *select_key_type_cmd =
90             "SELECT alias FROM CKM_TABLE WHERE "
91             //                1
92                 " dataType >= ? AND "
93             //                2
94                 " dataType <= ? AND "
95             //                           3
96                 " (restricted=0 OR label=?)";
97
98     const char *select_type_cmd =
99             //                                          1
100             "SELECT alias FROM CKM_TABLE WHERE dataType=? AND restricted=0 "
101             "UNION ALL "
102             //                                          2                            3
103             "SELECT alias FROM CKM_TABLE WHERE dataType=? AND restricted=1 AND label=?;";
104
105     const char *delete_alias_cmd =
106             //                                 1           2
107             "DELETE FROM CKM_TABLE WHERE alias=? AND label=?;";
108
109 // KEY_TABLE (label TEXT, key BLOB)
110
111     const char *db_create_key_cmd =
112             "CREATE TABLE KEY_TABLE("
113             "   label TEXT PRIMARY KEY,"
114             "   key BLOB NOT NULL"
115             ");";
116
117     const char *insert_key_cmd =
118             "INSERT INTO KEY_TABLE(label, key) VALUES (?, ?);";
119     const char *select_key_cmd =
120             "SELECT key FROM KEY_TABLE WHERE label=?;";
121     const char *delete_key_cmd =
122             "DELETE FROM KEY_TABLE WHERE label=?";
123 }
124
125 namespace CKM {
126 using namespace DB;
127     DBCrypto::DBCrypto(const std::string& path,
128                          const RawBuffer &rawPass) {
129         m_connection = NULL;
130         m_inUserTransaction = false;
131         Try {
132             m_connection = new SqlConnection(path, SqlConnection::Flag::Option::CRW);
133             m_connection->SetKey(rawPass);
134             initDatabase();
135         } Catch(SqlConnection::Exception::ConnectionBroken) {
136             LogError("Couldn't connect to database: " << path);
137             ReThrow(DBCrypto::Exception::InternalError);
138         } Catch(SqlConnection::Exception::InvalidArguments) {
139             LogError("Couldn't set the key for database");
140             ReThrow(DBCrypto::Exception::InternalError);
141         } Catch(SqlConnection::Exception::SyntaxError) {
142             LogError("Couldn't initiate the database");
143             ReThrow(DBCrypto::Exception::InternalError);
144         } Catch(SqlConnection::Exception::InternalError) {
145             LogError("Couldn't create the database");
146             ReThrow(DBCrypto::Exception::InternalError);
147         }
148     }
149
150     DBCrypto::DBCrypto(DBCrypto &&other) :
151             m_connection(other.m_connection),
152             m_inUserTransaction(other.m_inUserTransaction){
153         other.m_connection = NULL;
154         other.m_inUserTransaction = false;
155     }
156
157     DBCrypto::~DBCrypto() {
158         delete m_connection;
159     }
160
161     DBCrypto& DBCrypto::operator=(DBCrypto&& other) {
162         if (this == &other)
163             return *this;
164         delete m_connection;
165
166         m_connection = other.m_connection;
167         other.m_connection = NULL;
168
169         m_inUserTransaction = other.m_inUserTransaction;
170         other.m_inUserTransaction = false;
171
172         return *this;
173     }
174
175     void DBCrypto::createTable(
176             const char* create_cmd,
177             const char *table_name)
178     {
179         Try {
180             m_connection->ExecCommand(create_cmd);
181         } Catch(SqlConnection::Exception::SyntaxError) {
182             LogError("Couldn't create table : " << table_name << "!");
183             throw;
184         }
185     }
186
187     void DBCrypto::initDatabase() {
188         Transaction transaction(this);
189         if(!m_connection->CheckTableExist(main_table)) {
190             createTable(db_create_main_cmd, main_table);
191         }
192         if(!m_connection->CheckTableExist(key_table)) {
193             createTable(db_create_key_cmd, key_table);
194         }
195         transaction.commit();
196     }
197
198     bool DBCrypto::checkGlobalAliasExist(const std::string& alias) {
199         SqlConnection::DataCommandAutoPtr checkCmd =
200                 m_connection->PrepareDataCommand(select_check_global_alias_cmd);
201         checkCmd->BindString(1, alias.c_str());
202         if(checkCmd->Step()) {
203             LogDebug("Global alias '" << alias  << "' exists already for label "
204                     << checkCmd->GetColumnString(0));
205             return true;
206         } else
207             return false;
208     }
209
210     bool DBCrypto::checkAliasExist(
211             const std::string& alias,
212             const std::string& label) {
213         SqlConnection::DataCommandAutoPtr checkCmd =
214                 m_connection->PrepareDataCommand(select_check_alias_cmd);
215         checkCmd->BindString(1, alias.c_str());
216         checkCmd->BindString(2, label.c_str());
217         if(checkCmd->Step()) {
218             LogDebug("Private alias '" << alias  << "' exists already for type "
219                     << checkCmd->GetColumnInteger(0));
220             return true;
221         } else
222             return false;
223     }
224
225     void DBCrypto::saveDBRow(const DBRow &row){
226         Try {
227
228             //Sqlite does not support partial index in our version,
229             //so we do it by hand
230             Transaction transaction(this);
231             if((row.restricted == 1 && checkAliasExist(row.alias, row.smackLabel)) ||
232                     (row.restricted == 0 && checkGlobalAliasExist(row.alias))) {
233                 ThrowMsg(DBCrypto::Exception::AliasExists,
234                         "Alias exists for alias: " << row.alias
235                         << ", label: " << row.smackLabel);
236             }
237
238             SqlConnection::DataCommandAutoPtr insertCommand =
239                     m_connection->PrepareDataCommand(insert_main_cmd);
240             insertCommand->BindString(1, row.alias.c_str());
241             insertCommand->BindString(2, row.smackLabel.c_str());
242             insertCommand->BindInteger(3, row.restricted);
243             insertCommand->BindInteger(4, row.exportable);
244             insertCommand->BindInteger(5, static_cast<int>(row.dataType));
245             insertCommand->BindInteger(6, static_cast<int>(row.algorithmType));
246             insertCommand->BindInteger(7, row.encryptionScheme);
247             insertCommand->BindBlob(8, row.iv);
248             insertCommand->BindInteger(9, row.dataSize);
249             insertCommand->BindBlob(10, row.data);
250
251             insertCommand->Step();
252             transaction.commit();
253             return;
254
255         } Catch(SqlConnection::Exception::SyntaxError) {
256             LogError("Couldn't prepare insert statement");
257         } Catch(SqlConnection::Exception::InternalError) {
258             LogError("Couldn't execute insert statement");
259         }
260         ThrowMsg(DBCrypto::Exception::InternalError,
261                 "Couldn't save DBRow");
262     }
263
264     DBRow DBCrypto::getRow(const SqlConnection::DataCommandAutoPtr &selectCommand) {
265         DBRow row;
266         row.alias = selectCommand->GetColumnString(0);
267         row.smackLabel = selectCommand->GetColumnString(1);
268         row.restricted = selectCommand->GetColumnInteger(2);
269         row.exportable = selectCommand->GetColumnInteger(3);
270         row.dataType = static_cast<DBDataType>(selectCommand->GetColumnInteger(4));
271         row.algorithmType = static_cast<DBCMAlgType>(selectCommand->GetColumnInteger(5));
272         row.encryptionScheme = selectCommand->GetColumnInteger(6);
273         row.iv = selectCommand->GetColumnBlob(7);
274         row.dataSize = selectCommand->GetColumnInteger(8);
275         row.data = selectCommand->GetColumnBlob(9);
276         return row;
277     }
278
279     DBCrypto::DBRowOptional DBCrypto::getDBRow(
280         const Alias &alias,
281         const std::string &label,
282         DBDataType type)
283     {
284         Try {
285             Transaction transaction(this);
286             SqlConnection::DataCommandAutoPtr selectCommand =
287                     m_connection->PrepareDataCommand(select_alias_cmd);
288             selectCommand->BindString(1, alias.c_str());
289             selectCommand->BindInteger(2, static_cast<int>(type));
290             selectCommand->BindString(3, label.c_str());
291             selectCommand->BindString(4, alias.c_str());
292             selectCommand->BindInteger(5, static_cast<int>(type));
293
294             if(selectCommand->Step()) {
295                 transaction.commit();
296                 return DBRowOptional(getRow(selectCommand));
297             } else {
298                 return DBRowOptional();
299             }
300         } Catch (SqlConnection::Exception::InvalidColumn) {
301             LogError("Select statement invalid column error");
302         } Catch (SqlConnection::Exception::SyntaxError) {
303             LogError("Couldn't prepare select statement");
304         } Catch (SqlConnection::Exception::InternalError) {
305             LogError("Couldn't execute select statement");
306         }
307         ThrowMsg(DBCrypto::Exception::InternalError,
308                 "Couldn't get row for type " << static_cast<int>(type) <<
309                 " alias " << alias << " and label " << label);
310     }
311
312     DBCrypto::DBRowOptional DBCrypto::getKeyDBRow(
313         const Alias &alias,
314         const std::string &label)
315     {
316         Try{
317             Transaction transaction(this);
318             SqlConnection::DataCommandAutoPtr selectCommand =
319                     m_connection->PrepareDataCommand(select_key_alias_cmd);
320             selectCommand->BindString(1, alias.c_str());
321             selectCommand->BindInteger(2, static_cast<int>(DBDataType::DB_KEY_FIRST));
322             selectCommand->BindInteger(3, static_cast<int>(DBDataType::DB_KEY_LAST));
323             selectCommand->BindString(4, label.c_str());
324
325             if(selectCommand->Step()) {
326                 transaction.commit();
327                 return DBRowOptional(getRow(selectCommand));
328             } else {
329                 return DBRowOptional();
330             }
331         } Catch (SqlConnection::Exception::InvalidColumn) {
332             LogError("Select statement invalid column error");
333         } Catch (SqlConnection::Exception::SyntaxError) {
334             LogError("Couldn't prepare select statement");
335         } Catch (SqlConnection::Exception::InternalError) {
336             LogError("Couldn't execute select statement");
337         }
338         ThrowMsg(DBCrypto::Exception::InternalError,
339                 "Couldn't get Key for alias " << alias
340                 << " and label " << label);
341     }
342
343     void DBCrypto::getSingleType(
344             DBDataType type,
345             const std::string& label,
346             AliasVector& aliases)
347     {
348         Try{
349             SqlConnection::DataCommandAutoPtr selectCommand =
350                             m_connection->PrepareDataCommand(select_type_cmd);
351             selectCommand->BindInteger(1, static_cast<int>(type));
352             selectCommand->BindInteger(2, static_cast<int>(type));
353             selectCommand->BindString(3, label.c_str());
354
355             while(selectCommand->Step()) {
356                 Alias alias;
357                 alias = selectCommand->GetColumnString(0);
358                 aliases.push_back(alias);
359             }
360             return;
361         } Catch (SqlConnection::Exception::InvalidColumn) {
362             LogError("Select statement invalid column error");
363         } Catch (SqlConnection::Exception::SyntaxError) {
364             LogError("Couldn't prepare select statement");
365         } Catch (SqlConnection::Exception::InternalError) {
366             LogError("Couldn't execute select statement");
367         }
368         ThrowMsg(DBCrypto::Exception::InternalError,
369                 "Couldn't get type " << static_cast<int>(type)
370                 << " for label " << label);
371     }
372
373     void DBCrypto::getAliases(
374         DBDataType type,
375         const std::string& label,
376         AliasVector& aliases)
377     {
378         getSingleType(type, label, aliases);
379     }
380
381
382     void DBCrypto::getKeyAliases(
383         const std::string &label,
384         AliasVector &aliases)
385     {
386         Try{
387             Transaction transaction(this);
388             SqlConnection::DataCommandAutoPtr selectCommand =
389                             m_connection->PrepareDataCommand(select_key_type_cmd);
390             selectCommand->BindInteger(1, static_cast<int>(DBDataType::DB_KEY_FIRST));
391             selectCommand->BindInteger(2, static_cast<int>(DBDataType::DB_KEY_LAST));
392             selectCommand->BindString(3, label.c_str());
393
394             while(selectCommand->Step()) {
395                 Alias alias;
396                 alias = selectCommand->GetColumnString(0);
397                 aliases.push_back(alias);
398             }
399             transaction.commit();
400             return;
401         } Catch (SqlConnection::Exception::InvalidColumn) {
402             LogError("Select statement invalid column error");
403         } Catch (SqlConnection::Exception::SyntaxError) {
404             LogError("Couldn't prepare select statement");
405         } Catch (SqlConnection::Exception::InternalError) {
406             LogError("Couldn't execute select statement");
407         }
408         ThrowMsg(DBCrypto::Exception::InternalError,
409                 "Couldn't get key aliases for label " << label);
410     }
411
412     void DBCrypto::deleteDBRow(
413             const Alias &alias,
414             const std::string &label)
415     {
416         Try {
417             Transaction transaction(this);
418             SqlConnection::DataCommandAutoPtr deleteCommand =
419                     m_connection->PrepareDataCommand(delete_alias_cmd);
420             deleteCommand->BindString(1, alias.c_str());
421             deleteCommand->BindString(2, label.c_str());
422             deleteCommand->Step();
423             transaction.commit();
424             return;
425         } Catch (SqlConnection::Exception::SyntaxError) {
426             LogError("Couldn't prepare delete statement");
427         } Catch (SqlConnection::Exception::InternalError) {
428             LogError("Couldn't execute delete statement");
429         }
430         ThrowMsg(DBCrypto::Exception::InternalError,
431                 "Couldn't delete DBRow for alias " << alias
432                 << " and label " << label);
433     }
434
435     void DBCrypto::saveKey(
436             const std::string& label,
437             const RawBuffer &key)
438     {
439         Try {
440             Transaction transaction(this);
441             SqlConnection::DataCommandAutoPtr insertCommand =
442                     m_connection->PrepareDataCommand(insert_key_cmd);
443             insertCommand->BindString(1, label.c_str());
444             insertCommand->BindBlob(2, key);
445             insertCommand->Step();
446             transaction.commit();
447             return;
448         } Catch (SqlConnection::Exception::SyntaxError) {
449             LogError("Couldn't prepare insert key statement");
450         } Catch (SqlConnection::Exception::InternalError) {
451             LogError("Couldn't execute insert statement");
452         }
453         ThrowMsg(DBCrypto::Exception::InternalError,
454                 "Couldn't save key for label " << label);
455     }
456
457     DBCrypto::RawBufferOptional DBCrypto::getKey(
458             const std::string& label)
459     {
460         Try {
461             Transaction transaction(this);
462             SqlConnection::DataCommandAutoPtr selectCommand =
463                     m_connection->PrepareDataCommand(select_key_cmd);
464             selectCommand->BindString(1, label.c_str());
465
466             if (selectCommand->Step()) {
467                 transaction.commit();
468                 return RawBufferOptional(
469                         selectCommand->GetColumnBlob(0));
470             } else {
471                 transaction.commit();
472                 return RawBufferOptional();
473             }
474
475         } Catch (SqlConnection::Exception::InvalidColumn) {
476             LogError("Select statement invalid column error");
477         } Catch (SqlConnection::Exception::SyntaxError) {
478             LogError("Couldn't prepare insert key statement");
479         } Catch (SqlConnection::Exception::InternalError) {
480             LogError("Couldn't execute insert statement");
481         }
482         ThrowMsg(DBCrypto::Exception::InternalError,
483                 "Couldn't get key for label " << label);
484     }
485
486     void DBCrypto::deleteKey(const std::string& label) {
487         Try {
488             Transaction transaction(this);
489             SqlConnection::DataCommandAutoPtr deleteCommand =
490                     m_connection->PrepareDataCommand(delete_key_cmd);
491             deleteCommand->BindString(1, label.c_str());
492             deleteCommand->Step();
493             transaction.commit();
494             return;
495         } Catch (SqlConnection::Exception::SyntaxError) {
496             LogError("Couldn't prepare insert key statement");
497         } Catch (SqlConnection::Exception::InternalError) {
498             LogError("Couldn't execute insert statement");
499         }
500         ThrowMsg(DBCrypto::Exception::InternalError,
501                 "Couldn't delete key for label " << label);
502     }
503
504 } // CKM
505
506 #pragma GCC diagnostic pop