pib-sqlite3.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2019 Regents of the University of California.
4  *
5  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6  *
7  * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later version.
10  *
11  * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14  *
15  * You should have received copies of the GNU General Public License and GNU Lesser
16  * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20  */
21 
26 
27 #include <sqlite3.h>
28 #include <boost/filesystem.hpp>
29 #include <boost/algorithm/string.hpp>
30 
31 namespace ndn {
32 namespace security {
33 namespace pib {
34 
35 using util::Sqlite3Statement;
36 
37 static const std::string INITIALIZATION = R"SQL(
38 CREATE TABLE IF NOT EXISTS
39  tpmInfo(
40  tpm_locator BLOB
41  );
42 
43 CREATE TABLE IF NOT EXISTS
44  identities(
45  id INTEGER PRIMARY KEY,
46  identity BLOB NOT NULL,
47  is_default INTEGER DEFAULT 0
48  );
49 
50 CREATE UNIQUE INDEX IF NOT EXISTS
51  identityIndex ON identities(identity);
52 
53 CREATE TRIGGER IF NOT EXISTS
54  identity_default_before_insert_trigger
55  BEFORE INSERT ON identities
56  FOR EACH ROW
57  WHEN NEW.is_default=1
58  BEGIN
59  UPDATE identities SET is_default=0;
60  END;
61 
62 CREATE TRIGGER IF NOT EXISTS
63  identity_default_after_insert_trigger
64  AFTER INSERT ON identities
65  FOR EACH ROW
66  WHEN NOT EXISTS
67  (SELECT id
68  FROM identities
69  WHERE is_default=1)
70  BEGIN
71  UPDATE identities
72  SET is_default=1
73  WHERE identity=NEW.identity;
74  END;
75 
76 CREATE TRIGGER IF NOT EXISTS
77  identity_default_update_trigger
78  BEFORE UPDATE ON identities
79  FOR EACH ROW
80  WHEN NEW.is_default=1 AND OLD.is_default=0
81  BEGIN
82  UPDATE identities SET is_default=0;
83  END;
84 
85 CREATE TABLE IF NOT EXISTS
86  keys(
87  id INTEGER PRIMARY KEY,
88  identity_id INTEGER NOT NULL,
89  key_name BLOB NOT NULL,
90  key_bits BLOB NOT NULL,
91  is_default INTEGER DEFAULT 0,
92  FOREIGN KEY(identity_id)
93  REFERENCES identities(id)
94  ON DELETE CASCADE
95  ON UPDATE CASCADE
96  );
97 
98 CREATE UNIQUE INDEX IF NOT EXISTS
99  keyIndex ON keys(key_name);
100 
101 CREATE TRIGGER IF NOT EXISTS
102  key_default_before_insert_trigger
103  BEFORE INSERT ON keys
104  FOR EACH ROW
105  WHEN NEW.is_default=1
106  BEGIN
107  UPDATE keys
108  SET is_default=0
109  WHERE identity_id=NEW.identity_id;
110  END;
111 
112 CREATE TRIGGER IF NOT EXISTS
113  key_default_after_insert_trigger
114  AFTER INSERT ON keys
115  FOR EACH ROW
116  WHEN NOT EXISTS
117  (SELECT id
118  FROM keys
119  WHERE is_default=1
120  AND identity_id=NEW.identity_id)
121  BEGIN
122  UPDATE keys
123  SET is_default=1
124  WHERE key_name=NEW.key_name;
125  END;
126 
127 CREATE TRIGGER IF NOT EXISTS
128  key_default_update_trigger
129  BEFORE UPDATE ON keys
130  FOR EACH ROW
131  WHEN NEW.is_default=1 AND OLD.is_default=0
132  BEGIN
133  UPDATE keys
134  SET is_default=0
135  WHERE identity_id=NEW.identity_id;
136  END;
137 
138 
139 CREATE TABLE IF NOT EXISTS
140  certificates(
141  id INTEGER PRIMARY KEY,
142  key_id INTEGER NOT NULL,
143  certificate_name BLOB NOT NULL,
144  certificate_data BLOB NOT NULL,
145  is_default INTEGER DEFAULT 0,
146  FOREIGN KEY(key_id)
147  REFERENCES keys(id)
148  ON DELETE CASCADE
149  ON UPDATE CASCADE
150  );
151 
152 CREATE UNIQUE INDEX IF NOT EXISTS
153  certIndex ON certificates(certificate_name);
154 
155 CREATE TRIGGER IF NOT EXISTS
156  cert_default_before_insert_trigger
157  BEFORE INSERT ON certificates
158  FOR EACH ROW
159  WHEN NEW.is_default=1
160  BEGIN
161  UPDATE certificates
162  SET is_default=0
163  WHERE key_id=NEW.key_id;
164  END;
165 
166 CREATE TRIGGER IF NOT EXISTS
167  cert_default_after_insert_trigger
168  AFTER INSERT ON certificates
169  FOR EACH ROW
170  WHEN NOT EXISTS
171  (SELECT id
172  FROM certificates
173  WHERE is_default=1
174  AND key_id=NEW.key_id)
175  BEGIN
176  UPDATE certificates
177  SET is_default=1
178  WHERE certificate_name=NEW.certificate_name;
179  END;
180 
181 CREATE TRIGGER IF NOT EXISTS
182  cert_default_update_trigger
183  BEFORE UPDATE ON certificates
184  FOR EACH ROW
185  WHEN NEW.is_default=1 AND OLD.is_default=0
186  BEGIN
187  UPDATE certificates
188  SET is_default=0
189  WHERE key_id=NEW.key_id;
190  END;
191 )SQL";
192 
193 PibSqlite3::PibSqlite3(const std::string& location)
194 {
195  // Determine the path of PIB DB
196  boost::filesystem::path dbDir;
197  if (!location.empty()) {
198  dbDir = boost::filesystem::path(location);
199  }
200 #ifdef NDN_CXX_HAVE_TESTS
201  else if (getenv("TEST_HOME") != nullptr) {
202  dbDir = boost::filesystem::path(getenv("TEST_HOME")) / ".ndn";
203  }
204 #endif // NDN_CXX_HAVE_TESTS
205  else if (getenv("HOME") != nullptr) {
206  dbDir = boost::filesystem::path(getenv("HOME")) / ".ndn";
207  }
208  else {
209  dbDir = boost::filesystem::current_path() / ".ndn";
210  }
211  boost::filesystem::create_directories(dbDir);
212 
213  // Open PIB
214  int result = sqlite3_open_v2((dbDir / "pib.db").c_str(), &m_database,
215  SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
216 #ifdef NDN_CXX_DISABLE_SQLITE3_FS_LOCKING
217  "unix-dotfile"
218 #else
219  nullptr
220 #endif
221  );
222 
223  if (result != SQLITE_OK) {
224  NDN_THROW(PibImpl::Error("PIB database cannot be opened/created in " + dbDir.string()));
225  }
226 
227  // enable foreign key
228  sqlite3_exec(m_database, "PRAGMA foreign_keys=ON", nullptr, nullptr, nullptr);
229 
230  // initialize PIB tables
231  char* errmsg = nullptr;
232  result = sqlite3_exec(m_database, INITIALIZATION.c_str(), nullptr, nullptr, &errmsg);
233  if (result != SQLITE_OK && errmsg != nullptr) {
234  std::string what = "PIB database cannot be initialized: "s + errmsg;
235  sqlite3_free(errmsg);
236  NDN_THROW(PibImpl::Error(what));
237  }
238 }
239 
241 {
242  sqlite3_close(m_database);
243 }
244 
245 const std::string&
247 {
248  static std::string scheme = "pib-sqlite3";
249  return scheme;
250 }
251 
252 void
253 PibSqlite3::setTpmLocator(const std::string& tpmLocator)
254 {
255  Sqlite3Statement statement(m_database, "UPDATE tpmInfo SET tpm_locator=?");
256  statement.bind(1, tpmLocator, SQLITE_TRANSIENT);
257  statement.step();
258 
259  if (sqlite3_changes(m_database) == 0) {
260  // no row is updated, tpm_locator does not exist, insert it directly
261  Sqlite3Statement insertStatement(m_database, "INSERT INTO tpmInfo (tpm_locator) values (?)");
262  insertStatement.bind(1, tpmLocator, SQLITE_TRANSIENT);
263  insertStatement.step();
264  }
265 }
266 
267 std::string
269 {
270  Sqlite3Statement statement(m_database, "SELECT tpm_locator FROM tpmInfo");
271  int res = statement.step();
272  if (res == SQLITE_ROW)
273  return statement.getString(0);
274  else
275  return "";
276 }
277 
278 bool
279 PibSqlite3::hasIdentity(const Name& identity) const
280 {
281  Sqlite3Statement statement(m_database, "SELECT id FROM identities WHERE identity=?");
282  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
283  return statement.step() == SQLITE_ROW;
284 }
285 
286 void
288 {
289  if (!hasIdentity(identity)) {
290  Sqlite3Statement statement(m_database, "INSERT INTO identities (identity) values (?)");
291  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
292  statement.step();
293  }
294 
295  if (!hasDefaultIdentity()) {
296  setDefaultIdentity(identity);
297  }
298 }
299 
300 void
302 {
303  Sqlite3Statement statement(m_database, "DELETE FROM identities WHERE identity=?");
304  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
305  statement.step();
306 }
307 
308 void
310 {
311  Sqlite3Statement statement(m_database, "DELETE FROM identities");
312  statement.step();
313 }
314 
315 std::set<Name>
317 {
318  std::set<Name> identities;
319  Sqlite3Statement statement(m_database, "SELECT identity FROM identities");
320 
321  while (statement.step() == SQLITE_ROW)
322  identities.insert(Name(statement.getBlock(0)));
323 
324  return identities;
325 }
326 
327 void
329 {
330  Sqlite3Statement statement(m_database, "UPDATE identities SET is_default=1 WHERE identity=?");
331  statement.bind(1, identityName.wireEncode(), SQLITE_TRANSIENT);
332  statement.step();
333 }
334 
335 Name
337 {
338  Sqlite3Statement statement(m_database, "SELECT identity FROM identities WHERE is_default=1");
339 
340  if (statement.step() == SQLITE_ROW)
341  return Name(statement.getBlock(0));
342  else
343  NDN_THROW(Pib::Error("No default identity"));
344 }
345 
346 bool
347 PibSqlite3::hasDefaultIdentity() const
348 {
349  Sqlite3Statement statement(m_database, "SELECT identity FROM identities WHERE is_default=1");
350  return (statement.step() == SQLITE_ROW);
351 }
352 
353 bool
354 PibSqlite3::hasKey(const Name& keyName) const
355 {
356  Sqlite3Statement statement(m_database, "SELECT id FROM keys WHERE key_name=?");
357  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
358 
359  return (statement.step() == SQLITE_ROW);
360 }
361 
362 void
363 PibSqlite3::addKey(const Name& identity, const Name& keyName,
364  const uint8_t* key, size_t keyLen)
365 {
366  // ensure identity exists
367  addIdentity(identity);
368 
369  if (!hasKey(keyName)) {
370  Sqlite3Statement statement(m_database,
371  "INSERT INTO keys (identity_id, key_name, key_bits) "
372  "VALUES ((SELECT id FROM identities WHERE identity=?), ?, ?)");
373  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
374  statement.bind(2, keyName.wireEncode(), SQLITE_TRANSIENT);
375  statement.bind(3, key, keyLen, SQLITE_STATIC);
376  statement.step();
377  }
378  else {
379  Sqlite3Statement statement(m_database,
380  "UPDATE keys SET key_bits=? WHERE key_name=?");
381  statement.bind(1, key, keyLen, SQLITE_STATIC);
382  statement.bind(2, keyName.wireEncode(), SQLITE_TRANSIENT);
383  statement.step();
384  }
385 
386  if (!hasDefaultKeyOfIdentity(identity)) {
387  setDefaultKeyOfIdentity(identity, keyName);
388  }
389 }
390 
391 void
393 {
394  Sqlite3Statement statement(m_database, "DELETE FROM keys WHERE key_name=?");
395  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
396  statement.step();
397 }
398 
399 Buffer
400 PibSqlite3::getKeyBits(const Name& keyName) const
401 {
402  Sqlite3Statement statement(m_database, "SELECT key_bits FROM keys WHERE key_name=?");
403  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
404 
405  if (statement.step() == SQLITE_ROW)
406  return Buffer(statement.getBlob(0), statement.getSize(0));
407  else
408  NDN_THROW(Pib::Error("Key `" + keyName.toUri() + "` does not exist"));
409 }
410 
411 std::set<Name>
412 PibSqlite3::getKeysOfIdentity(const Name& identity) const
413 {
414  std::set<Name> keyNames;
415 
416  Sqlite3Statement statement(m_database,
417  "SELECT key_name "
418  "FROM keys JOIN identities ON keys.identity_id=identities.id "
419  "WHERE identities.identity=?");
420  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
421 
422  while (statement.step() == SQLITE_ROW) {
423  keyNames.insert(Name(statement.getBlock(0)));
424  }
425 
426  return keyNames;
427 }
428 
429 void
430 PibSqlite3::setDefaultKeyOfIdentity(const Name& identity, const Name& keyName)
431 {
432  if (!hasKey(keyName)) {
433  NDN_THROW(Pib::Error("Key `" + keyName.toUri() + "` does not exist"));
434  }
435 
436  Sqlite3Statement statement(m_database, "UPDATE keys SET is_default=1 WHERE key_name=?");
437  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
438  statement.step();
439 }
440 
441 Name
443 {
444  if (!hasIdentity(identity)) {
445  NDN_THROW(Pib::Error("Identity `" + identity.toUri() + "` does not exist"));
446  }
447 
448  Sqlite3Statement statement(m_database,
449  "SELECT key_name "
450  "FROM keys JOIN identities ON keys.identity_id=identities.id "
451  "WHERE identities.identity=? AND keys.is_default=1");
452  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
453 
454  if (statement.step() == SQLITE_ROW) {
455  return Name(statement.getBlock(0));
456  }
457  else
458  NDN_THROW(Pib::Error("No default key for identity `" + identity.toUri() + "`"));
459 }
460 
461 bool
462 PibSqlite3::hasDefaultKeyOfIdentity(const Name& identity) const
463 {
464  Sqlite3Statement statement(m_database,
465  "SELECT key_name "
466  "FROM keys JOIN identities ON keys.identity_id=identities.id "
467  "WHERE identities.identity=? AND keys.is_default=1");
468  statement.bind(1, identity.wireEncode(), SQLITE_TRANSIENT);
469 
470  return (statement.step() == SQLITE_ROW);
471 }
472 
473 bool
474 PibSqlite3::hasCertificate(const Name& certName) const
475 {
476  Sqlite3Statement statement(m_database, "SELECT id FROM certificates WHERE certificate_name=?");
477  statement.bind(1, certName.wireEncode(), SQLITE_TRANSIENT);
478  return (statement.step() == SQLITE_ROW);
479 }
480 
481 void
483 {
484  // ensure key exists
485  const Block& content = certificate.getContent();
486  addKey(certificate.getIdentity(), certificate.getKeyName(), content.value(), content.value_size());
487 
488  if (!hasCertificate(certificate.getName())) {
489  Sqlite3Statement statement(m_database,
490  "INSERT INTO certificates "
491  "(key_id, certificate_name, certificate_data) "
492  "VALUES ((SELECT id FROM keys WHERE key_name=?), ?, ?)");
493  statement.bind(1, certificate.getKeyName().wireEncode(), SQLITE_TRANSIENT);
494  statement.bind(2, certificate.getName().wireEncode(), SQLITE_TRANSIENT);
495  statement.bind(3, certificate.wireEncode(), SQLITE_STATIC);
496  statement.step();
497  }
498  else {
499  Sqlite3Statement statement(m_database,
500  "UPDATE certificates SET certificate_data=? WHERE certificate_name=?");
501  statement.bind(1, certificate.wireEncode(), SQLITE_STATIC);
502  statement.bind(2, certificate.getName().wireEncode(), SQLITE_TRANSIENT);
503  statement.step();
504  }
505 
506  if (!hasDefaultCertificateOfKey(certificate.getKeyName())) {
507  setDefaultCertificateOfKey(certificate.getKeyName(), certificate.getName());
508  }
509 }
510 
511 void
513 {
514  Sqlite3Statement statement(m_database, "DELETE FROM certificates WHERE certificate_name=?");
515  statement.bind(1, certName.wireEncode(), SQLITE_TRANSIENT);
516  statement.step();
517 }
518 
520 PibSqlite3::getCertificate(const Name& certName) const
521 {
522  Sqlite3Statement statement(m_database,
523  "SELECT certificate_data FROM certificates WHERE certificate_name=?");
524  statement.bind(1, certName.wireEncode(), SQLITE_TRANSIENT);
525 
526  if (statement.step() == SQLITE_ROW)
527  return v2::Certificate(statement.getBlock(0));
528  else
529  NDN_THROW(Pib::Error("Certificate `" + certName.toUri() + "` does not exit"));
530 }
531 
532 std::set<Name>
534 {
535  std::set<Name> certNames;
536 
537  Sqlite3Statement statement(m_database,
538  "SELECT certificate_name "
539  "FROM certificates JOIN keys ON certificates.key_id=keys.id "
540  "WHERE keys.key_name=?");
541  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
542 
543  while (statement.step() == SQLITE_ROW)
544  certNames.insert(Name(statement.getBlock(0)));
545 
546  return certNames;
547 }
548 
549 void
550 PibSqlite3::setDefaultCertificateOfKey(const Name& keyName, const Name& certName)
551 {
552  if (!hasCertificate(certName)) {
553  NDN_THROW(Pib::Error("Certificate `" + certName.toUri() + "` does not exist"));
554  }
555 
556  Sqlite3Statement statement(m_database,
557  "UPDATE certificates SET is_default=1 WHERE certificate_name=?");
558  statement.bind(1, certName.wireEncode(), SQLITE_TRANSIENT);
559  statement.step();
560 }
561 
564 {
565  Sqlite3Statement statement(m_database,
566  "SELECT certificate_data "
567  "FROM certificates JOIN keys ON certificates.key_id=keys.id "
568  "WHERE certificates.is_default=1 AND keys.key_name=?");
569  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
570 
571  if (statement.step() == SQLITE_ROW)
572  return v2::Certificate(statement.getBlock(0));
573  else
574  NDN_THROW(Pib::Error("No default certificate for key `" + keyName.toUri() + "`"));
575 }
576 
577 bool
578 PibSqlite3::hasDefaultCertificateOfKey(const Name& keyName) const
579 {
580  Sqlite3Statement statement(m_database,
581  "SELECT certificate_data "
582  "FROM certificates JOIN keys ON certificates.key_id=keys.id "
583  "WHERE certificates.is_default=1 AND keys.key_name=?");
584  statement.bind(1, keyName.wireEncode(), SQLITE_TRANSIENT);
585 
586  return statement.step() == SQLITE_ROW;
587 }
588 
589 } // namespace pib
590 } // namespace security
591 } // namespace ndn
represents a non-semantic error
Definition: pib-impl.hpp:49
void removeCertificate(const Name &certName) final
Remove a certificate with name certName.
Definition: data.cpp:26
Name getDefaultKeyOfIdentity(const Name &identity) const final
The certificate following the certificate format naming convention.
Definition: certificate.hpp:81
represents a semantic error
Definition: pib.hpp:56
Name getKeyName() const
Get key name.
Definition: certificate.cpp:81
std::set< Name > getCertificatesOfKey(const Name &keyName) const final
Get a list of certificate names of a key with id keyName.
void clearIdentities() final
Erasing all certificates, keys, and identities.
void addKey(const Name &identity, const Name &keyName, const uint8_t *key, size_t keyLen) final
Add a key.
size_t value_size() const noexcept
Return the size of TLV-VALUE, aka TLV-LENGTH.
Definition: block.cpp:307
size_t wireEncode(EncodingImpl< TAG > &encoder) const
Fast encoding or block size estimation.
Definition: name.cpp:125
Represents a TLV element of NDN packet format.
Definition: block.hpp:42
std::set< Name > getIdentities() const final
Get the name of all the identities.
void setTpmLocator(const std::string &tpmLocator) final
Set the corresponding TPM information to tpmLocator.
#define NDN_THROW(e)
Definition: exception.hpp:61
v2::Certificate getCertificate(const Name &certName) const final
Get a certificate with name certName.
std::string toUri() const
Get URI representation of the name.
Definition: name.cpp:116
std::string getTpmLocator() const final
Get TPM Locator.
size_t wireEncode(EncodingImpl< TAG > &encoder, bool wantUnsignedPortionOnly=false) const
Prepend wire encoding to encoder in NDN Packet Format v0.2.
Definition: data.cpp:48
Buffer getKeyBits(const Name &keyName) const final
Get the key bits of a key with name keyName.
static const std::string & getScheme()
void removeIdentity(const Name &identity) final
Remove an identity and related keys and certificates.
void setDefaultIdentity(const Name &identityName) final
Set an identity with name identityName as the default identity.
void setDefaultCertificateOfKey(const Name &keyName, const Name &certName) final
Set a cert with name certName as the default of a key with keyName.
Name getDefaultIdentity() const final
Get the default identity.
Name getIdentity() const
Get identity name.
Definition: certificate.cpp:87
Represents an absolute name.
Definition: name.hpp:43
~PibSqlite3()
Destruct and cleanup internal state.
PibSqlite3(const std::string &location="")
Create sqlite3-based PIB backed.
void addIdentity(const Name &identity) final
Add an identity.
void addCertificate(const v2::Certificate &certificate) final
Add a certificate.
const Name & getName() const
Get name.
Definition: data.hpp:124
v2::Certificate getDefaultCertificateOfKey(const Name &keyName) const final
const uint8_t * value() const noexcept
Return a raw pointer to the beginning of TLV-VALUE.
Definition: block.cpp:301
bool hasKey(const Name &keyName) const final
Check the existence of a key with keyName.
const Block & getContent() const
Get Content.
Definition: data.cpp:232
void removeKey(const Name &keyName) final
Remove a key with keyName and related certificates.
void setDefaultKeyOfIdentity(const Name &identity, const Name &keyName) final
Set an key with keyName as the default key of an identity with name identity.
std::set< Name > getKeysOfIdentity(const Name &identity) const final
Get all the key names of an identity with name identity.
static const std::string INITIALIZATION
Definition: pib-sqlite3.cpp:37
bool hasIdentity(const Name &identity) const final
Check the existence of an identity.
General-purpose automatically managed/resized buffer.
Definition: buffer.hpp:40
bool hasCertificate(const Name &certName) const final
Check the existence of a certificate with name certName.