Aktualizr
C++ SOTA Client
sqlstorage.cc
1 #include "sqlstorage.h"
2 
3 #include <sys/stat.h>
4 #include <iostream>
5 #include <memory>
6 #include <string>
7 #include <utility>
8 
9 #include "logging/logging.h"
10 #include "sql_utils.h"
11 #include "utilities/utils.h"
12 
13 boost::filesystem::path SQLStorage::dbPath() const { return config_.sqldb_path.get(config_.path); }
14 
15 // find metadata with version set to -1 (e.g. after migration) and assign proper version to it
16 void SQLStorage::cleanMetaVersion(Uptane::RepositoryType repo, Uptane::Role role) {
17  SQLite3Guard db = dbConnection();
18 
19  if (!db.beginTransaction()) {
20  LOG_ERROR << "Can't start transaction: " << db.errmsg();
21  return;
22  }
23 
24  auto statement = db.prepareStatement<int, int, int>(
25  "SELECT meta FROM meta WHERE (repo=? AND meta_type=? AND version=?);", static_cast<int>(repo), role.ToInt(), -1);
26 
27  int result = statement.step();
28 
29  if (result == SQLITE_DONE) {
30  LOG_TRACE << "meta with role " << role.ToString() << " in repo " << Uptane::RepoString(repo)
31  << " not present in db";
32  return;
33  } else if (result != SQLITE_ROW) {
34  LOG_ERROR << "Can't get meta: " << db.errmsg();
35  return;
36  }
37  std::string meta = std::string(reinterpret_cast<const char*>(sqlite3_column_blob(statement.get(), 0)));
38 
39  int version = Uptane::extractVersionUntrusted(meta);
40  if (version < 0) {
41  LOG_ERROR << "Corrupted metadata";
42  return;
43  }
44 
45  // in there is already metadata with such version delete it
46  statement = db.prepareStatement<int, int, int>("DELETE FROM meta WHERE (repo=? AND meta_type=? AND version=?);",
47  static_cast<int>(repo), role.ToInt(), version);
48 
49  if (statement.step() != SQLITE_DONE) {
50  LOG_ERROR << "Can't clear metadata: " << db.errmsg();
51  return;
52  }
53 
54  statement = db.prepareStatement<int, int, int, int>(
55  "UPDATE meta SET version = ? WHERE (repo=? AND meta_type=? AND version=?);", version, static_cast<int>(repo),
56  role.ToInt(), -1);
57 
58  if (statement.step() != SQLITE_DONE) {
59  LOG_ERROR << "Can't update metadata: " << db.errmsg();
60  return;
61  }
62 
63  db.commitTransaction();
64 }
65 
66 SQLStorage::SQLStorage(const StorageConfig& config, bool readonly) : INvStorage(config), readonly_(readonly) {
67  boost::filesystem::path db_parent_path = dbPath().parent_path();
68  if (!boost::filesystem::is_directory(db_parent_path)) {
69  Utils::createDirectories(db_parent_path, S_IRWXU);
70  } else {
71  struct stat st {};
72  stat(db_parent_path.c_str(), &st);
73  if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
74  throw StorageException("Storage directory has unsafe permissions");
75  }
76  if ((st.st_mode & (S_IRGRP | S_IROTH)) != 0) {
77  // Remove read permissions for group and others
78  chmod(db_parent_path.c_str(), S_IRWXU);
79  }
80  }
81 
82  if (!dbMigrate()) {
83  LOG_ERROR << "SQLite database migration failed";
84  // Continue to run anyway, it can't be worse
85  }
86 
87  try {
88  cleanMetaVersion(Uptane::RepositoryType::Director, Uptane::Role::Root());
89  cleanMetaVersion(Uptane::RepositoryType::Images, Uptane::Role::Root());
90  } catch (...) {
91  LOG_ERROR << "SQLite database metadata version migration failed";
92  }
93 }
94 
95 SQLite3Guard SQLStorage::dbConnection() {
96  SQLite3Guard db(dbPath(), readonly_);
97  if (db.get_rc() != SQLITE_OK) {
98  throw SQLException(std::string("Can't open database: ") + db.errmsg());
99  }
100  return db;
101 }
102 
103 void SQLStorage::storePrimaryKeys(const std::string& public_key, const std::string& private_key) {
104  SQLite3Guard db = dbConnection();
105 
106  auto statement = db.prepareStatement<std::string>(
107  "INSERT OR REPLACE INTO primary_keys(unique_mark,public,private) VALUES (0,?,?);", public_key, private_key);
108  if (statement.step() != SQLITE_DONE) {
109  LOG_ERROR << "Can't set primary keys: " << db.errmsg();
110  return;
111  }
112 }
113 
114 bool SQLStorage::loadPrimaryKeys(std::string* public_key, std::string* private_key) {
115  return loadPrimaryPublic(public_key) && loadPrimaryPrivate(private_key);
116 }
117 
118 bool SQLStorage::loadPrimaryPublic(std::string* public_key) {
119  SQLite3Guard db = dbConnection();
120 
121  auto statement = db.prepareStatement("SELECT public FROM primary_keys LIMIT 1;");
122 
123  int result = statement.step();
124  if (result == SQLITE_DONE) {
125  LOG_TRACE << "No public key in db";
126  return false;
127  } else if (result != SQLITE_ROW) {
128  LOG_ERROR << "Can't get public key: " << db.errmsg();
129  return false;
130  }
131 
132  auto pub = statement.get_result_col_str(0);
133  if (pub == boost::none) {
134  return false;
135  }
136 
137  if (public_key != nullptr) {
138  *public_key = std::move(pub.value());
139  }
140 
141  return true;
142 }
143 
144 bool SQLStorage::loadPrimaryPrivate(std::string* private_key) {
145  SQLite3Guard db = dbConnection();
146 
147  auto statement = db.prepareStatement("SELECT private FROM primary_keys LIMIT 1;");
148 
149  int result = statement.step();
150  if (result == SQLITE_DONE) {
151  LOG_TRACE << "No private key in db";
152  return false;
153  } else if (result != SQLITE_ROW) {
154  LOG_ERROR << "Can't get private key: " << db.errmsg();
155  return false;
156  }
157 
158  auto priv = statement.get_result_col_str(0);
159  if (priv == boost::none) {
160  return false;
161  }
162 
163  if (private_key != nullptr) {
164  *private_key = std::move(priv.value());
165  }
166 
167  return true;
168 }
169 
170 void SQLStorage::clearPrimaryKeys() {
171  SQLite3Guard db = dbConnection();
172 
173  if (db.exec("DELETE FROM primary_keys;", nullptr, nullptr) != SQLITE_OK) {
174  LOG_ERROR << "Can't clear primary keys: " << db.errmsg();
175  return;
176  }
177 }
178 
179 void SQLStorage::storeTlsCreds(const std::string& ca, const std::string& cert, const std::string& pkey) {
180  storeTlsCa(ca);
181  storeTlsCert(cert);
182  storeTlsPkey(pkey);
183 }
184 
185 void SQLStorage::storeTlsCa(const std::string& ca) {
186  SQLite3Guard db = dbConnection();
187 
188  if (!db.beginTransaction()) {
189  LOG_ERROR << "Can't start transaction: " << db.errmsg();
190  return;
191  }
192 
193  auto statement = db.prepareStatement("SELECT count(*) FROM tls_creds;");
194  if (statement.step() != SQLITE_ROW) {
195  LOG_ERROR << "Can't get count of tls_creds table: " << db.errmsg();
196  return;
197  }
198 
199  const char* req;
200  if (statement.get_result_col_int(0) != 0) {
201  req = "UPDATE OR REPLACE tls_creds SET ca_cert = ?;";
202  } else {
203  req = "INSERT INTO tls_creds(ca_cert) VALUES (?);";
204  }
205 
206  statement = db.prepareStatement<SQLBlob>(req, SQLBlob(ca));
207  if (statement.step() != SQLITE_DONE) {
208  LOG_ERROR << "Can't set ca_cert: " << db.errmsg();
209  return;
210  }
211 
212  db.commitTransaction();
213 }
214 
215 void SQLStorage::storeTlsCert(const std::string& cert) {
216  SQLite3Guard db = dbConnection();
217 
218  if (!db.beginTransaction()) {
219  LOG_ERROR << "Can't start transaction: " << db.errmsg();
220  return;
221  }
222 
223  auto statement = db.prepareStatement("SELECT count(*) FROM tls_creds;");
224  if (statement.step() != SQLITE_ROW) {
225  LOG_ERROR << "Can't get count of tls_creds table: " << db.errmsg();
226  return;
227  }
228 
229  const char* req;
230  if (statement.get_result_col_int(0) != 0) {
231  req = "UPDATE OR REPLACE tls_creds SET client_cert = ?;";
232  } else {
233  req = "INSERT INTO tls_creds(client_cert) VALUES (?);";
234  }
235 
236  statement = db.prepareStatement<SQLBlob>(req, SQLBlob(cert));
237  if (statement.step() != SQLITE_DONE) {
238  LOG_ERROR << "Can't set client_cert: " << db.errmsg();
239  return;
240  }
241 
242  db.commitTransaction();
243 }
244 
245 void SQLStorage::storeTlsPkey(const std::string& pkey) {
246  SQLite3Guard db = dbConnection();
247 
248  if (!db.beginTransaction()) {
249  LOG_ERROR << "Can't start transaction: " << db.errmsg();
250  return;
251  }
252  auto statement = db.prepareStatement("SELECT count(*) FROM tls_creds;");
253  if (statement.step() != SQLITE_ROW) {
254  LOG_ERROR << "Can't get count of tls_creds table: " << db.errmsg();
255  return;
256  }
257 
258  const char* req;
259  if (statement.get_result_col_int(0) != 0) {
260  req = "UPDATE OR REPLACE tls_creds SET client_pkey = ?;";
261  } else {
262  req = "INSERT INTO tls_creds(client_pkey) VALUES (?);";
263  }
264 
265  statement = db.prepareStatement<SQLBlob>(req, SQLBlob(pkey));
266  if (statement.step() != SQLITE_DONE) {
267  LOG_ERROR << "Can't set client_pkey: " << db.errmsg();
268  return;
269  }
270 
271  db.commitTransaction();
272 }
273 
274 bool SQLStorage::loadTlsCreds(std::string* ca, std::string* cert, std::string* pkey) {
275  SQLite3Guard db = dbConnection();
276 
277  if (!db.beginTransaction()) {
278  LOG_ERROR << "Can't start transaction: " << db.errmsg();
279  return false;
280  }
281  auto statement = db.prepareStatement("SELECT ca_cert, client_cert, client_pkey FROM tls_creds LIMIT 1;");
282 
283  int result = statement.step();
284  if (result == SQLITE_DONE) {
285  LOG_TRACE << "Tls creds not present";
286  return false;
287  } else if (result != SQLITE_ROW) {
288  LOG_ERROR << "Can't get tls_creds: " << db.errmsg();
289  return false;
290  }
291 
292  std::string ca_v, cert_v, pkey_v;
293  try {
294  ca_v = statement.get_result_col_str(0).value();
295  cert_v = statement.get_result_col_str(1).value();
296  pkey_v = statement.get_result_col_str(2).value();
297  } catch (const boost::bad_optional_access&) {
298  return false;
299  }
300 
301  if (ca != nullptr) {
302  *ca = std::move(ca_v);
303  }
304  if (cert != nullptr) {
305  *cert = std::move(cert_v);
306  }
307  if (pkey != nullptr) {
308  *pkey = std::move(pkey_v);
309  }
310 
311  db.commitTransaction();
312 
313  return true;
314 }
315 
316 void SQLStorage::clearTlsCreds() {
317  SQLite3Guard db = dbConnection();
318 
319  if (db.exec("DELETE FROM tls_creds;", nullptr, nullptr) != SQLITE_OK) {
320  LOG_ERROR << "Can't clear tls_creds: " << db.errmsg();
321  return;
322  }
323 }
324 
325 bool SQLStorage::loadTlsCa(std::string* ca) {
326  SQLite3Guard db = dbConnection();
327 
328  auto statement = db.prepareStatement("SELECT ca_cert FROM tls_creds LIMIT 1;");
329 
330  int result = statement.step();
331  if (result == SQLITE_DONE) {
332  LOG_TRACE << "ca_cert not present";
333  return false;
334  } else if (result != SQLITE_ROW) {
335  LOG_ERROR << "Can't get ca_cert: " << db.errmsg();
336  return false;
337  }
338 
339  auto ca_r = statement.get_result_col_str(0);
340  if (ca_r == boost::none) {
341  return false;
342  }
343 
344  if (ca != nullptr) {
345  *ca = std::move(ca_r.value());
346  }
347 
348  return true;
349 }
350 
351 bool SQLStorage::loadTlsCert(std::string* cert) {
352  SQLite3Guard db = dbConnection();
353 
354  auto statement = db.prepareStatement("SELECT client_cert FROM tls_creds LIMIT 1;");
355 
356  int result = statement.step();
357  if (result == SQLITE_DONE) {
358  LOG_TRACE << "client_cert not present in db";
359  return false;
360  } else if (result != SQLITE_ROW) {
361  LOG_ERROR << "Can't get client_cert: " << db.errmsg();
362  return false;
363  }
364 
365  auto cert_r = statement.get_result_col_str(0);
366  if (cert_r == boost::none) {
367  return false;
368  }
369 
370  if (cert != nullptr) {
371  *cert = std::move(cert_r.value());
372  }
373 
374  return true;
375 }
376 
377 bool SQLStorage::loadTlsPkey(std::string* pkey) {
378  SQLite3Guard db = dbConnection();
379 
380  auto statement = db.prepareStatement("SELECT client_pkey FROM tls_creds LIMIT 1;");
381 
382  int result = statement.step();
383  if (result == SQLITE_DONE) {
384  LOG_TRACE << "client_pkey not present in db";
385  return false;
386  } else if (result != SQLITE_ROW) {
387  LOG_ERROR << "Can't get client_pkey: " << db.errmsg();
388  return false;
389  }
390 
391  auto pkey_r = statement.get_result_col_str(0);
392  if (pkey_r == boost::none) {
393  return false;
394  }
395 
396  if (pkey != nullptr) {
397  *pkey = std::move(pkey_r.value());
398  }
399 
400  return true;
401 }
402 
403 void SQLStorage::storeRoot(const std::string& data, Uptane::RepositoryType repo, Uptane::Version version) {
404  SQLite3Guard db = dbConnection();
405 
406  if (!db.beginTransaction()) {
407  LOG_ERROR << "Can't start transaction: " << db.errmsg();
408  return;
409  }
410 
411  auto del_statement =
412  db.prepareStatement<int, int, int>("DELETE FROM meta WHERE (repo=? AND meta_type=? AND version=?);",
413  static_cast<int>(repo), Uptane::Role::Root().ToInt(), version.version());
414 
415  if (del_statement.step() != SQLITE_DONE) {
416  LOG_ERROR << "Can't clear root metadata: " << db.errmsg();
417  return;
418  }
419 
420  auto ins_statement = db.prepareStatement<SQLBlob, int, int, int>("INSERT INTO meta VALUES (?, ?, ?, ?);",
421  SQLBlob(data), static_cast<int>(repo),
422  Uptane::Role::Root().ToInt(), version.version());
423 
424  if (ins_statement.step() != SQLITE_DONE) {
425  LOG_ERROR << "Can't add metadata: " << db.errmsg();
426  return;
427  }
428 
429  db.commitTransaction();
430 }
431 
432 void SQLStorage::storeNonRoot(const std::string& data, Uptane::RepositoryType repo, Uptane::Role role) {
433  SQLite3Guard db = dbConnection();
434 
435  if (!db.beginTransaction()) {
436  LOG_ERROR << "Can't start transaction: " << db.errmsg();
437  return;
438  }
439 
440  auto del_statement = db.prepareStatement<int, int>("DELETE FROM meta WHERE (repo=? AND meta_type=?);",
441  static_cast<int>(repo), role.ToInt());
442 
443  if (del_statement.step() != SQLITE_DONE) {
444  LOG_ERROR << "Can't clear metadata: " << db.errmsg();
445  return;
446  }
447 
448  auto ins_statement =
449  db.prepareStatement<SQLBlob, int, int, int>("INSERT INTO meta VALUES (?, ?, ?, ?);", SQLBlob(data),
450  static_cast<int>(repo), role.ToInt(), Uptane::Version().version());
451 
452  if (ins_statement.step() != SQLITE_DONE) {
453  LOG_ERROR << "Can't add metadata: " << db.errmsg();
454  return;
455  }
456 
457  db.commitTransaction();
458 }
459 
460 bool SQLStorage::loadRoot(std::string* data, Uptane::RepositoryType repo, Uptane::Version version) {
461  SQLite3Guard db = dbConnection();
462 
463  // version < 0 => latest metadata requested
464  if (version.version() < 0) {
465  auto statement = db.prepareStatement<int, int>(
466  "SELECT meta FROM meta WHERE (repo=? AND meta_type=?) ORDER BY version DESC LIMIT 1;", static_cast<int>(repo),
467  Uptane::Role::Root().ToInt());
468  int result = statement.step();
469 
470  if (result == SQLITE_DONE) {
471  LOG_TRACE << "Meta not present";
472  return false;
473  } else if (result != SQLITE_ROW) {
474  LOG_ERROR << "Can't get meta: " << db.errmsg();
475  return false;
476  }
477  if (data != nullptr) {
478  *data = std::string(reinterpret_cast<const char*>(sqlite3_column_blob(statement.get(), 0)));
479  }
480  } else {
481  auto statement =
482  db.prepareStatement<int, int, int>("SELECT meta FROM meta WHERE (repo=? AND meta_type=? AND version=?);",
483  static_cast<int>(repo), Uptane::Role::Root().ToInt(), version.version());
484 
485  int result = statement.step();
486 
487  if (result == SQLITE_DONE) {
488  LOG_TRACE << "Meta not present";
489  return false;
490  } else if (result != SQLITE_ROW) {
491  LOG_ERROR << "Can't get meta: " << db.errmsg();
492  return false;
493  }
494  if (data != nullptr) {
495  *data = std::string(reinterpret_cast<const char*>(sqlite3_column_blob(statement.get(), 0)));
496  }
497  }
498 
499  return true;
500 }
501 
502 bool SQLStorage::loadNonRoot(std::string* data, Uptane::RepositoryType repo, Uptane::Role role) {
503  SQLite3Guard db = dbConnection();
504 
505  auto statement = db.prepareStatement<int, int>(
506  "SELECT meta FROM meta WHERE (repo=? AND meta_type=?) ORDER BY version DESC LIMIT 1;", static_cast<int>(repo),
507  role.ToInt());
508  int result = statement.step();
509 
510  if (result == SQLITE_DONE) {
511  LOG_TRACE << "Meta not present";
512  return false;
513  } else if (result != SQLITE_ROW) {
514  LOG_ERROR << "Can't get meta: " << db.errmsg();
515  return false;
516  }
517  if (data != nullptr) {
518  *data = std::string(reinterpret_cast<const char*>(sqlite3_column_blob(statement.get(), 0)));
519  }
520 
521  return true;
522 }
523 
524 void SQLStorage::clearNonRootMeta(Uptane::RepositoryType repo) {
525  SQLite3Guard db = dbConnection();
526 
527  auto del_statement =
528  db.prepareStatement<int>("DELETE FROM meta WHERE (repo=? AND meta_type != 0);", static_cast<int>(repo));
529 
530  if (del_statement.step() != SQLITE_DONE) {
531  LOG_ERROR << "Can't clear metadata: " << db.errmsg();
532  }
533 }
534 
535 void SQLStorage::clearMetadata() {
536  SQLite3Guard db = dbConnection();
537 
538  if (db.exec("DELETE FROM meta;", nullptr, nullptr) != SQLITE_OK) {
539  LOG_ERROR << "Can't clear metadata: " << db.errmsg();
540  return;
541  }
542 }
543 
544 void SQLStorage::storeDeviceId(const std::string& device_id) {
545  SQLite3Guard db = dbConnection();
546 
547  auto statement = db.prepareStatement<std::string>(
548  "INSERT OR REPLACE INTO device_info(unique_mark,device_id,is_registered) VALUES(0,?,0);", device_id);
549  if (statement.step() != SQLITE_DONE) {
550  LOG_ERROR << "Can't set device ID: " << db.errmsg();
551  return;
552  }
553 }
554 
555 bool SQLStorage::loadDeviceId(std::string* device_id) {
556  SQLite3Guard db = dbConnection();
557 
558  auto statement = db.prepareStatement("SELECT device_id FROM device_info LIMIT 1;");
559 
560  int result = statement.step();
561  if (result == SQLITE_DONE) {
562  LOG_TRACE << "device_id not present in db";
563  return false;
564  } else if (result != SQLITE_ROW) {
565  LOG_ERROR << "Can't get device ID: " << db.errmsg();
566  return false;
567  }
568 
569  auto did = statement.get_result_col_str(0);
570  if (did == boost::none) {
571  LOG_ERROR << "Empty device ID" << db.errmsg();
572  return false;
573  }
574 
575  if (device_id != nullptr) {
576  *device_id = std::move(did.value());
577  }
578 
579  return true;
580 }
581 
582 void SQLStorage::clearDeviceId() {
583  SQLite3Guard db = dbConnection();
584 
585  if (db.exec("DELETE FROM device_info;", nullptr, nullptr) != SQLITE_OK) {
586  LOG_ERROR << "Can't clear device ID: " << db.errmsg();
587  return;
588  }
589 }
590 
591 void SQLStorage::storeEcuRegistered() {
592  SQLite3Guard db = dbConnection();
593 
594  if (!db.beginTransaction()) {
595  LOG_ERROR << "Can't start transaction: " << db.errmsg();
596  return;
597  }
598 
599  auto statement = db.prepareStatement("SELECT count(*) FROM device_info;");
600  if (statement.step() != SQLITE_ROW) {
601  throw std::runtime_error("Could not get device_info count");
602  }
603  if (statement.get_result_col_int(0) != 1) {
604  throw std::runtime_error("Cannot set ecu registered if no device_info set");
605  }
606 
607  std::string req = "UPDATE device_info SET is_registered = 1";
608  if (db.exec(req.c_str(), nullptr, nullptr) != SQLITE_OK) {
609  LOG_ERROR << "Can't set is_registered: " << db.errmsg();
610  return;
611  }
612 
613  db.commitTransaction();
614 }
615 
616 bool SQLStorage::loadEcuRegistered() {
617  SQLite3Guard db = dbConnection();
618 
619  auto statement = db.prepareStatement("SELECT is_registered FROM device_info LIMIT 1;");
620 
621  int result = statement.step();
622  if (result == SQLITE_DONE) {
623  return false;
624  } else if (result != SQLITE_ROW) {
625  LOG_ERROR << "Can't get is_registered in device_info " << db.errmsg();
626  return false;
627  }
628 
629  return statement.get_result_col_int(0) != 0;
630 }
631 
632 void SQLStorage::clearEcuRegistered() {
633  SQLite3Guard db = dbConnection();
634 
635  // note: if the table is empty, nothing is done but that's fine
636  std::string req = "UPDATE device_info SET is_registered = 0";
637  if (db.exec(req.c_str(), nullptr, nullptr) != SQLITE_OK) {
638  LOG_ERROR << "Can't set is_registered: " << db.errmsg();
639  return;
640  }
641 }
642 
643 void SQLStorage::storeEcuSerials(const EcuSerials& serials) {
644  if (serials.size() >= 1) {
645  SQLite3Guard db = dbConnection();
646 
647  if (!db.beginTransaction()) {
648  LOG_ERROR << "Can't start transaction: " << db.errmsg();
649  return;
650  }
651 
652  if (db.exec("DELETE FROM ecu_serials;", nullptr, nullptr) != SQLITE_OK) {
653  LOG_ERROR << "Can't clear ecu_serials: " << db.errmsg();
654  return;
655  }
656 
657  std::string serial = serials[0].first.ToString();
658  std::string hwid = serials[0].second.ToString();
659  {
660  auto statement =
661  db.prepareStatement<std::string, std::string>("INSERT INTO ecu_serials VALUES (?,?,1);", serial, hwid);
662  if (statement.step() != SQLITE_DONE) {
663  LOG_ERROR << "Can't set ecu_serial: " << db.errmsg();
664  return;
665  }
666  }
667 
668  EcuSerials::const_iterator it;
669  for (it = serials.begin() + 1; it != serials.end(); it++) {
670  auto statement = db.prepareStatement<std::string, std::string>("INSERT INTO ecu_serials VALUES (?,?,0);",
671  it->first.ToString(), it->second.ToString());
672 
673  if (statement.step() != SQLITE_DONE) {
674  LOG_ERROR << "Can't set ecu_serial: " << db.errmsg();
675  return;
676  }
677  }
678 
679  db.commitTransaction();
680  }
681 }
682 
683 bool SQLStorage::loadEcuSerials(EcuSerials* serials) {
684  SQLite3Guard db = dbConnection();
685 
686  auto statement = db.prepareStatement("SELECT serial, hardware_id FROM ecu_serials ORDER BY is_primary DESC;");
687  int statement_state;
688 
689  EcuSerials new_serials;
690  bool empty = true;
691  while ((statement_state = statement.step()) == SQLITE_ROW) {
692  try {
693  new_serials.emplace_back(Uptane::EcuSerial(statement.get_result_col_str(0).value()),
694  Uptane::HardwareIdentifier(statement.get_result_col_str(1).value()));
695  empty = false;
696  } catch (const boost::bad_optional_access&) {
697  return false;
698  }
699  }
700 
701  if (statement_state != SQLITE_DONE) {
702  LOG_ERROR << "Can't get ecu_serials: " << db.errmsg();
703  return false;
704  }
705 
706  if (serials != nullptr) {
707  *serials = std::move(new_serials);
708  }
709 
710  return !empty;
711 }
712 
713 void SQLStorage::clearEcuSerials() {
714  SQLite3Guard db = dbConnection();
715 
716  if (db.exec("DELETE FROM ecu_serials;", nullptr, nullptr) != SQLITE_OK) {
717  LOG_ERROR << "Can't clear ecu_serials: " << db.errmsg();
718  return;
719  }
720 }
721 
722 void SQLStorage::storeMisconfiguredEcus(const std::vector<MisconfiguredEcu>& ecus) {
723  if (ecus.size() >= 1) {
724  SQLite3Guard db = dbConnection();
725 
726  if (!db.beginTransaction()) {
727  LOG_ERROR << "Can't start transaction: " << db.errmsg();
728  return;
729  }
730 
731  if (db.exec("DELETE FROM misconfigured_ecus;", nullptr, nullptr) != SQLITE_OK) {
732  LOG_ERROR << "Can't clear misconfigured_ecus: " << db.errmsg();
733  return;
734  }
735 
736  std::vector<MisconfiguredEcu>::const_iterator it;
737  for (it = ecus.begin(); it != ecus.end(); it++) {
738  auto statement = db.prepareStatement<std::string, std::string, int>(
739  "INSERT INTO misconfigured_ecus VALUES (?,?,?);", it->serial.ToString(), it->hardware_id.ToString(),
740  static_cast<int>(it->state));
741 
742  if (statement.step() != SQLITE_DONE) {
743  LOG_ERROR << "Can't set misconfigured_ecus: " << db.errmsg();
744  return;
745  }
746  }
747 
748  db.commitTransaction();
749  }
750 }
751 
752 bool SQLStorage::loadMisconfiguredEcus(std::vector<MisconfiguredEcu>* ecus) {
753  SQLite3Guard db = dbConnection();
754 
755  auto statement = db.prepareStatement("SELECT serial, hardware_id, state FROM misconfigured_ecus;");
756  int statement_state;
757 
758  std::vector<MisconfiguredEcu> new_ecus;
759  bool empty = true;
760  while ((statement_state = statement.step()) == SQLITE_ROW) {
761  try {
762  new_ecus.emplace_back(Uptane::EcuSerial(statement.get_result_col_str(0).value()),
763  Uptane::HardwareIdentifier(statement.get_result_col_str(1).value()),
764  static_cast<EcuState>(statement.get_result_col_int(2)));
765  empty = false;
766  } catch (const boost::bad_optional_access&) {
767  return false;
768  }
769  }
770 
771  if (statement_state != SQLITE_DONE) {
772  LOG_ERROR << "Can't get misconfigured_ecus: " << db.errmsg();
773  return false;
774  }
775 
776  if (ecus != nullptr) {
777  *ecus = std::move(new_ecus);
778  }
779 
780  return !empty;
781 }
782 
783 void SQLStorage::clearMisconfiguredEcus() {
784  SQLite3Guard db = dbConnection();
785 
786  if (db.exec("DELETE FROM misconfigured_ecus;", nullptr, nullptr) != SQLITE_OK) {
787  LOG_ERROR << "Can't clear misconfigured_ecus: " << db.errmsg();
788  return;
789  }
790 }
791 
792 void SQLStorage::storeInstalledVersions(const std::vector<Uptane::Target>& installed_versions,
793  const std::string& current_hash) {
794  if (installed_versions.size() >= 1) {
795  SQLite3Guard db = dbConnection();
796 
797  if (!db.beginTransaction()) {
798  LOG_ERROR << "Can't start transaction: " << db.errmsg();
799  return;
800  }
801 
802  if (db.exec("DELETE FROM installed_versions;", nullptr, nullptr) != SQLITE_OK) {
803  LOG_ERROR << "Can't clear installed_versions: " << db.errmsg();
804  return;
805  }
806 
807  std::vector<Uptane::Target>::const_iterator it;
808  for (it = installed_versions.cbegin(); it != installed_versions.cend(); it++) {
809  std::string sql = "INSERT INTO installed_versions VALUES (?,?,?,?);";
810  std::string hash = it->sha256Hash();
811  std::string filename = it->filename();
812  bool is_current = current_hash == it->sha256Hash();
813  int64_t size = it->length();
814  auto statement = db.prepareStatement<std::string, std::string, int, int>(
815  sql, hash, filename, static_cast<int>(is_current), static_cast<int>(size));
816 
817  if (statement.step() != SQLITE_DONE) {
818  LOG_ERROR << "Can't set installed_versions: " << db.errmsg();
819  return;
820  }
821  }
822 
823  db.commitTransaction();
824  }
825 }
826 
827 std::string SQLStorage::loadInstalledVersions(std::vector<Uptane::Target>* installed_versions) {
828  SQLite3Guard db = dbConnection();
829 
830  std::string current_hash;
831  auto statement = db.prepareStatement("SELECT name, hash, length, is_current FROM installed_versions;");
832  int statement_state;
833 
834  std::vector<Uptane::Target> new_installed_versions;
835  std::string new_hash;
836  while ((statement_state = statement.step()) == SQLITE_ROW) {
837  try {
838  Json::Value installed_version;
839  auto name = statement.get_result_col_str(0).value();
840  auto hash = statement.get_result_col_str(1).value();
841  auto length = statement.get_result_col_int(2);
842  auto is_current = statement.get_result_col_int(3) != 0;
843 
844  installed_version["hashes"]["sha256"] = hash;
845  installed_version["length"] = Json::UInt64(length);
846  if (is_current) {
847  new_hash = hash;
848  }
849  std::string filename = name;
850  new_installed_versions.emplace_back(filename, installed_version);
851  } catch (const boost::bad_optional_access&) {
852  LOG_ERROR << "Incompleted installed version, keeping old one";
853  return current_hash;
854  }
855  }
856 
857  if (new_hash != "") {
858  current_hash = new_hash;
859  }
860 
861  if (statement_state != SQLITE_DONE) {
862  LOG_ERROR << "Can't get installed_versions: " << db.errmsg();
863  return current_hash;
864  }
865 
866  if (installed_versions != nullptr) {
867  *installed_versions = std::move(new_installed_versions);
868  }
869 
870  return current_hash;
871 }
872 
873 void SQLStorage::clearInstalledVersions() {
874  SQLite3Guard db = dbConnection();
875 
876  if (db.exec("DELETE FROM installed_versions;", nullptr, nullptr) != SQLITE_OK) {
877  LOG_ERROR << "Can't clear installed_versions: " << db.errmsg();
878  return;
879  }
880 }
881 
882 void SQLStorage::storeInstallationResult(const data::OperationResult& result) {
883  SQLite3Guard db = dbConnection();
884 
885  auto statement = db.prepareStatement<std::string, int, std::string>(
886  "INSERT OR REPLACE INTO installation_result (unique_mark, id, result_code, result_text) VALUES (0,?,?,?);",
887  result.id, static_cast<int>(result.result_code), result.result_text);
888  if (statement.step() != SQLITE_DONE) {
889  LOG_ERROR << "Can't set installation_result: " << db.errmsg();
890  return;
891  }
892 }
893 
894 bool SQLStorage::loadInstallationResult(data::OperationResult* result) {
895  SQLite3Guard db = dbConnection();
896 
897  auto statement = db.prepareStatement("SELECT id, result_code, result_text FROM installation_result LIMIT 1;");
898  int statement_result = statement.step();
899  if (statement_result == SQLITE_DONE) {
900  LOG_TRACE << "installation_result not present in db";
901  return false;
902  } else if (statement_result != SQLITE_ROW) {
903  LOG_ERROR << "Can't get installation_result: " << db.errmsg();
904  return false;
905  }
906 
907  std::string id;
908  int64_t result_code;
909  std::string result_text;
910  try {
911  id = statement.get_result_col_str(0).value();
912  result_code = statement.get_result_col_int(1);
913  result_text = statement.get_result_col_str(2).value();
914  } catch (const boost::bad_optional_access&) {
915  return false;
916  }
917 
918  if (result != nullptr) {
919  result->id = std::move(id);
920  result->result_code = static_cast<data::UpdateResultCode>(result_code);
921  result->result_text = std::move(result_text);
922  }
923 
924  return true;
925 }
926 
927 void SQLStorage::clearInstallationResult() {
928  SQLite3Guard db = dbConnection();
929 
930  if (db.exec("DELETE FROM installation_result;", nullptr, nullptr) != SQLITE_OK) {
931  LOG_ERROR << "Can't clear installation_result: " << db.errmsg();
932  return;
933  }
934 }
935 
937  public:
938  SQLTargetWHandle(const SQLStorage& storage, std::string filename, size_t size)
939  : db_(storage.dbPath()),
940  filename_(std::move(filename)),
941  expected_size_(size),
942  written_size_(0),
943  closed_(false),
944  blob_(nullptr) {
945  StorageTargetWHandle::WriteError exc("could not save file " + filename_ + " to sql storage");
946 
947  if (db_.get_rc() != SQLITE_OK) {
948  LOG_ERROR << "Can't open database: " << db_.errmsg();
949  throw exc;
950  }
951 
952  // allocate a zero blob
953  if (!db_.beginTransaction()) {
954  throw exc;
955  }
956 
957  auto statement = db_.prepareStatement<std::string, SQLZeroBlob>(
958  "INSERT OR REPLACE INTO target_images (filename, image_data) VALUES (?, ?);", filename_,
959  SQLZeroBlob{expected_size_});
960 
961  if (statement.step() != SQLITE_DONE) {
962  LOG_ERROR << "Statement step failure: " << db_.errmsg();
963  throw exc;
964  }
965 
966  // open the created blob for writing
967  sqlite3_int64 row_id = sqlite3_last_insert_rowid(db_.get());
968 
969  if (sqlite3_blob_open(db_.get(), "main", "target_images", "image_data", row_id, 1, &blob_) != SQLITE_OK) {
970  LOG_ERROR << "Could not open blob " << db_.errmsg();
971  throw exc;
972  }
973  }
974 
975  ~SQLTargetWHandle() override {
976  if (!closed_) {
977  LOG_WARNING << "Handle for file " << filename_ << " has not been committed or aborted, forcing abort";
978  SQLTargetWHandle::wabort();
979  }
980  }
981 
982  size_t wfeed(const uint8_t* buf, size_t size) override {
983  if (sqlite3_blob_write(blob_, buf, static_cast<int>(size), static_cast<int>(written_size_)) != SQLITE_OK) {
984  LOG_ERROR << "Could not write in blob: " << db_.errmsg();
985  return 0;
986  }
987  written_size_ += size;
988  return size;
989  }
990 
991  void wcommit() override {
992  closed_ = true;
993  sqlite3_blob_close(blob_);
994  blob_ = nullptr;
995  if (!db_.commitTransaction()) {
996  throw StorageTargetWHandle::WriteError("could not save file " + filename_ + " to sql storage");
997  }
998  }
999 
1000  void wabort() noexcept override {
1001  closed_ = true;
1002  if (blob_ != nullptr) {
1003  sqlite3_blob_close(blob_);
1004  blob_ = nullptr;
1005  }
1006 
1007  db_.rollbackTransaction();
1008  }
1009 
1010  private:
1011  SQLite3Guard db_;
1012  const std::string filename_;
1013  size_t expected_size_;
1014  size_t written_size_;
1015  bool closed_;
1016  sqlite3_blob* blob_;
1017 };
1018 
1019 std::unique_ptr<StorageTargetWHandle> SQLStorage::allocateTargetFile(bool from_director, const std::string& filename,
1020  size_t size) {
1021  (void)from_director;
1022 
1023  return std::unique_ptr<StorageTargetWHandle>(new SQLTargetWHandle(*this, filename, size));
1024 }
1025 
1027  public:
1028  SQLTargetRHandle(const SQLStorage& storage, const std::string& filename)
1029  : db_(storage.dbPath()), filename_(filename), size_(0), read_size_(0), closed_(false), blob_(nullptr) {
1030  StorageTargetRHandle::ReadError exc("could not read file " + filename_ + " from sql storage");
1031 
1032  if (db_.get_rc() != SQLITE_OK) {
1033  LOG_ERROR << "Can't open database: " << db_.errmsg();
1034  throw exc;
1035  }
1036 
1037  if (!db_.beginTransaction()) {
1038  throw exc;
1039  }
1040 
1041  auto statement = db_.prepareStatement<std::string>("SELECT rowid FROM target_images WHERE filename = ?;", filename);
1042 
1043  int err = statement.step();
1044  if (err == SQLITE_DONE) {
1045  LOG_ERROR << "No such file in db: " + filename_;
1046  throw exc;
1047  }
1048  if (err != SQLITE_ROW) {
1049  LOG_ERROR << "Statement step failure: " << db_.errmsg();
1050  throw exc;
1051  }
1052 
1053  auto row_id = statement.get_result_col_int(0);
1054 
1055  if (sqlite3_blob_open(db_.get(), "main", "target_images", "image_data", row_id, 0, &blob_) != SQLITE_OK) {
1056  LOG_ERROR << "Could not open blob: " << db_.errmsg();
1057  throw exc;
1058  }
1059  size_ = static_cast<size_t>(sqlite3_blob_bytes(blob_));
1060 
1061  if (!db_.commitTransaction()) {
1062  throw exc;
1063  }
1064  }
1065 
1066  ~SQLTargetRHandle() override {
1067  if (!closed_) {
1068  SQLTargetRHandle::rclose();
1069  }
1070  }
1071 
1072  size_t rsize() const override { return size_; }
1073 
1074  size_t rread(uint8_t* buf, size_t size) override {
1075  if (read_size_ + size > size_) {
1076  size = size_ - read_size_;
1077  }
1078  if (size == 0) {
1079  return 0;
1080  }
1081 
1082  if (sqlite3_blob_read(blob_, buf, static_cast<int>(size), static_cast<int>(read_size_)) != SQLITE_OK) {
1083  LOG_ERROR << "Could not read from blob: " << db_.errmsg();
1084  return 0;
1085  }
1086  read_size_ += size;
1087 
1088  return size;
1089  }
1090 
1091  void rclose() noexcept override {
1092  sqlite3_blob_close(blob_);
1093  closed_ = true;
1094  }
1095 
1096  private:
1097  SQLite3Guard db_;
1098  const std::string filename_;
1099  size_t size_;
1100  size_t read_size_;
1101  bool closed_;
1102  sqlite3_blob* blob_;
1103 };
1104 
1105 std::unique_ptr<StorageTargetRHandle> SQLStorage::openTargetFile(const std::string& filename) {
1106  return std::unique_ptr<StorageTargetRHandle>(new SQLTargetRHandle(*this, filename));
1107 }
1108 
1109 void SQLStorage::removeTargetFile(const std::string& filename) {
1110  SQLite3Guard db = dbConnection();
1111 
1112  auto statement = db.prepareStatement<std::string>("DELETE FROM target_images WHERE filename=?;", filename);
1113 
1114  if (statement.step() != SQLITE_DONE) {
1115  LOG_ERROR << "Statement step failure: " << db.errmsg();
1116  throw std::runtime_error("Could not remove target file");
1117  }
1118 
1119  if (sqlite3_changes(db.get()) != 1) {
1120  throw std::runtime_error("Target file " + filename + " not found");
1121  }
1122 }
1123 
1124 void SQLStorage::cleanUp() { boost::filesystem::remove_all(dbPath()); }
1125 
1126 std::string SQLStorage::getTableSchemaFromDb(const std::string& tablename) {
1127  SQLite3Guard db = dbConnection();
1128 
1129  auto statement = db.prepareStatement<std::string>(
1130  "SELECT sql FROM sqlite_master WHERE type='table' AND tbl_name=? LIMIT 1;", tablename);
1131 
1132  if (statement.step() != SQLITE_ROW) {
1133  LOG_ERROR << "Can't get schema of " << tablename << ": " << db.errmsg();
1134  return "";
1135  }
1136 
1137  auto schema = statement.get_result_col_str(0);
1138  if (schema == boost::none) {
1139  return "";
1140  }
1141 
1142  return schema.value() + ";";
1143 }
1144 
1145 bool SQLStorage::dbMigrate() {
1146  SQLite3Guard db = dbConnection();
1147 
1148  DbVersion schema_version = getVersion();
1149 
1150  if (schema_version == DbVersion::kInvalid) {
1151  LOG_ERROR << "Sqlite database file is invalid.";
1152  return false;
1153  }
1154 
1155  auto schema_num_version = static_cast<int32_t>(schema_version);
1156  if (schema_num_version == current_schema_version) {
1157  return true;
1158  }
1159 
1160  if (readonly_) {
1161  LOG_ERROR << "Database is opened in readonly mode and cannot be migrated to latest version";
1162  return false;
1163  }
1164 
1165  if (schema_num_version > current_schema_version) {
1166  LOG_ERROR << "Only forward migrations are supported. You cannot migrate to an older schema.";
1167  return false;
1168  }
1169 
1170  for (int32_t k = schema_num_version + 1; k <= current_schema_version; k++) {
1171  if (db.exec(schema_migrations.at(static_cast<size_t>(k)), nullptr, nullptr) != SQLITE_OK) {
1172  LOG_ERROR << "Can't migrate db from version " << (k - 1) << " to version " << k << ": " << db.errmsg();
1173  return false;
1174  }
1175  }
1176 
1177  return true;
1178 }
1179 
1180 DbVersion SQLStorage::getVersion() {
1181  SQLite3Guard db = dbConnection();
1182 
1183  try {
1184  auto statement = db.prepareStatement("SELECT count(*) FROM sqlite_master WHERE type='table';");
1185  if (statement.step() != SQLITE_ROW) {
1186  LOG_ERROR << "Can't get tables count: " << db.errmsg();
1187  return DbVersion::kInvalid;
1188  }
1189 
1190  if (statement.get_result_col_int(0) == 0) {
1191  return DbVersion::kEmpty;
1192  }
1193 
1194  statement = db.prepareStatement("SELECT version FROM version LIMIT 1;");
1195 
1196  if (statement.step() != SQLITE_ROW) {
1197  LOG_ERROR << "Can't get database version: " << db.errmsg();
1198  return DbVersion::kInvalid;
1199  }
1200 
1201  try {
1202  return DbVersion(statement.get_result_col_int(0));
1203  } catch (const std::exception&) {
1204  return DbVersion::kInvalid;
1205  }
1206  } catch (const SQLException&) {
1207  return DbVersion::kInvalid;
1208  }
1209 }
General data structures.
Definition: types.cc:6
Metadata version numbers.
Definition: tuf.h:57
TUF Roles.
Definition: tuf.h:25
RepositoryType
This must match the repo_type table in sqlstorage.
Definition: tuf.h:18
UpdateResultCode
Result of an update.
Definition: types.h:122