blob: bb88cc7fbba6bcde6170fa764111fdec5a994489 [file] [log] [blame]
Zhiyi Zhang91c846b2017-04-12 14:16:31 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -07003 * Copyright (c) 2017-2019, Regents of the University of California.
Zhiyi Zhang91c846b2017-04-12 14:16:31 -07004 *
5 * This file is part of ndncert, a certificate management system based on NDN.
6 *
7 * ndncert is free software: you can redistribute it and/or modify it under the terms
8 * of the GNU General Public License as published by the Free Software Foundation, either
9 * version 3 of the License, or (at your option) any later version.
10 *
11 * ndncert 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 General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License along with
16 * ndncert, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * See AUTHORS.md for complete list of ndncert authors and contributors.
19 */
20
21#include "ca-sqlite.hpp"
22#include <ndn-cxx/util/sqlite3-statement.hpp>
23
24#include <sqlite3.h>
25#include <boost/filesystem.hpp>
26
27namespace ndn {
28namespace ndncert {
29
30const std::string
31CaSqlite::STORAGE_TYPE = "ca-storage-sqlite3";
32
33NDNCERT_REGISTER_CA_STORAGE(CaSqlite);
34
35using namespace ndn::util;
36
37static const std::string INITIALIZATION = R"_DBTEXT_(
38CREATE TABLE IF NOT EXISTS
39 CertRequests(
40 id INTEGER PRIMARY KEY,
41 request_id TEXT NOT NULL,
42 ca_name BLOB NOT NULL,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070043 status INTEGER NOT NULL,
44 challenge_status TEXT,
Zhiyi Zhang91c846b2017-04-12 14:16:31 -070045 cert_key_name BLOB NOT NULL,
46 cert_request BLOB NOT NULL,
47 challenge_type TEXT,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -070048 challenge_secrets TEXT,
49 challenge_tp TEXT,
50 remaining_tries INTEGER,
51 remaining_time INTEGER
Zhiyi Zhang91c846b2017-04-12 14:16:31 -070052 );
53CREATE UNIQUE INDEX IF NOT EXISTS
54 CertRequestIdIndex ON CertRequests(request_id);
55CREATE UNIQUE INDEX IF NOT EXISTS
56 CertRequestKeyNameIndex ON CertRequests(cert_key_name);
57
58CREATE TABLE IF NOT EXISTS
59 IssuedCerts(
60 id INTEGER PRIMARY KEY,
61 cert_id TEXT NOT NULL,
62 cert_key_name BLOB NOT NULL,
63 cert BLOB NOT NULL
64 );
65CREATE UNIQUE INDEX IF NOT EXISTS
66 IssuedCertRequestIdIndex ON IssuedCerts(cert_id);
67CREATE UNIQUE INDEX IF NOT EXISTS
68 IssuedCertKeyNameIndex ON IssuedCerts(cert_key_name);
69)_DBTEXT_";
70
71CaSqlite::CaSqlite(const std::string& location)
72 : CaStorage()
73{
74 // Determine the path of sqlite db
75 boost::filesystem::path dbDir;
76 if (!location.empty()) {
77 dbDir = boost::filesystem::path(location);
78 }
Zhiyi Zhang91c846b2017-04-12 14:16:31 -070079 else if (getenv("HOME") != nullptr) {
80 dbDir = boost::filesystem::path(getenv("HOME")) / ".ndn";
81 }
82 else {
83 dbDir = boost::filesystem::current_path() / ".ndn";
84 }
85 boost::filesystem::create_directories(dbDir);
86
87 // open and initialize database
88 int result = sqlite3_open_v2((dbDir / "ndncert-ca.db").c_str(), &m_database,
89 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
90#ifdef NDN_CXX_DISABLE_SQLITE3_FS_LOCKING
91 "unix-dotfile"
92#else
93 nullptr
94#endif
95 );
96 if (result != SQLITE_OK)
97 BOOST_THROW_EXCEPTION(Error("CaSqlite DB cannot be opened/created: " + dbDir.string()));
98
99 // initialize database specific tables
100 char* errorMessage = nullptr;
101 result = sqlite3_exec(m_database, INITIALIZATION.c_str(),
102 nullptr, nullptr, &errorMessage);
103 if (result != SQLITE_OK && errorMessage != nullptr) {
104 sqlite3_free(errorMessage);
105 BOOST_THROW_EXCEPTION(Error("CaSqlite DB cannot be initialized"));
106 }
107}
108
109CaSqlite::~CaSqlite()
110{
111 sqlite3_close(m_database);
112}
113
114CertificateRequest
115CaSqlite::getRequest(const std::string& requestId)
116{
117 Sqlite3Statement statement(m_database,
118 R"_SQLTEXT_(SELECT *
119 FROM CertRequests where request_id = ?)_SQLTEXT_");
120 statement.bind(1, requestId, SQLITE_TRANSIENT);
121
122 if (statement.step() == SQLITE_ROW) {
123 Name caName(statement.getBlock(2));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700124 int status = statement.getInt(3);
125 std::string challengeStatus = statement.getString(4);
126 security::v2::Certificate cert(statement.getBlock(6));
127 std::string challengeType = statement.getString(7);
128 std::string challengeSecrets = statement.getString(8);
129 std::string challengeTp = statement.getString(9);
130 int remainingTries = statement.getInt(10);
131 int remainingTime = statement.getInt(11);
132 return CertificateRequest(caName, requestId, status, challengeStatus, challengeType,
133 challengeTp, remainingTime, remainingTries,
134 convertString2Json(challengeSecrets), cert);
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700135 }
136 else {
137 BOOST_THROW_EXCEPTION(Error("Request " + requestId + " cannot be fetched from database"));
138 }
139}
140
141void
142CaSqlite::addRequest(const CertificateRequest& request)
143{
144 Sqlite3Statement statement1(m_database,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700145 R"_SQLTEXT_(SELECT * FROM CertRequests where cert_key_name = ?)_SQLTEXT_");
146 statement1.bind(1, request.m_cert.getKeyName().wireEncode(), SQLITE_TRANSIENT);
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700147 if (statement1.step() == SQLITE_ROW) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700148 BOOST_THROW_EXCEPTION(Error("Request for " + request.m_cert.getKeyName().toUri() + " already exists"));
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700149 return;
150 }
151
152 Sqlite3Statement statement2(m_database,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700153 R"_SQLTEXT_(SELECT * FROM IssuedCerts where cert_key_name = ?)_SQLTEXT_");
154 statement2.bind(1, request.m_cert.getKeyName().wireEncode(), SQLITE_TRANSIENT);
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700155 if (statement2.step() == SQLITE_ROW) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700156 BOOST_THROW_EXCEPTION(Error("Cert for " + request.m_cert.getKeyName().toUri() + " already exists"));
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700157 return;
158 }
159
160 Sqlite3Statement statement(m_database,
161 R"_SQLTEXT_(INSERT INTO CertRequests (request_id, ca_name, status,
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700162 challenge_status, cert_key_name, cert_request, challenge_type, challenge_secrets,
163 challenge_tp, remaining_tries, remaining_time)
164 values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?))_SQLTEXT_");
165 statement.bind(1, request.m_requestId, SQLITE_TRANSIENT);
166 statement.bind(2, request.m_caName.wireEncode(), SQLITE_TRANSIENT);
167 statement.bind(3, request.m_status);
168 statement.bind(4, request.m_challengeStatus, SQLITE_TRANSIENT);
169 statement.bind(5, request.m_cert.getKeyName().wireEncode(), SQLITE_TRANSIENT);
170 statement.bind(6, request.m_cert.wireEncode(), SQLITE_TRANSIENT);
171 statement.bind(7, request.m_challengeType, SQLITE_TRANSIENT);
172 statement.bind(8, convertJson2String(request.m_challengeSecrets), SQLITE_TRANSIENT);
173 statement.bind(9, request.m_challengeTp, SQLITE_TRANSIENT);
174 statement.bind(10, request.m_remainingTries);
175 statement.bind(11, request.m_remainingTime);
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700176
177 if (statement.step() != SQLITE_DONE) {
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700178 BOOST_THROW_EXCEPTION(Error("Request " + request.m_requestId + " cannot be added to database"));
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700179 }
180}
181
182void
183CaSqlite::updateRequest(const CertificateRequest& request)
184{
185 Sqlite3Statement statement(m_database,
186 R"_SQLTEXT_(UPDATE CertRequests
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700187 SET status = ?, challenge_status = ?, challenge_type = ?, challenge_secrets = ?,
188 challenge_tp = ?, remaining_tries = ?, remaining_time = ?
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700189 WHERE request_id = ?)_SQLTEXT_");
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700190 statement.bind(1, request.m_status);
191 statement.bind(2, request.m_challengeStatus, SQLITE_TRANSIENT);
192 statement.bind(3, request.m_challengeType, SQLITE_TRANSIENT);
193 statement.bind(4, convertJson2String(request.m_challengeSecrets), SQLITE_TRANSIENT);
194 statement.bind(5, request.m_challengeTp, SQLITE_TRANSIENT);
195 statement.bind(6, request.m_remainingTries);
196 statement.bind(7, request.m_remainingTime);
197 statement.bind(8, request.m_requestId, SQLITE_TRANSIENT);
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700198
199 if (statement.step() != SQLITE_DONE) {
200 addRequest(request);
201 }
202}
203
Zhiyi Zhangae123bf2017-04-14 12:24:53 -0700204std::list<CertificateRequest>
205CaSqlite::listAllRequests()
206{
207 std::list<CertificateRequest> result;
208 Sqlite3Statement statement(m_database, R"_SQLTEXT_(SELECT * FROM CertRequests)_SQLTEXT_");
209
210 while(statement.step() == SQLITE_ROW) {
211 std::string requestId = statement.getString(1);
212 Name caName(statement.getBlock(2));
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700213 int status = statement.getInt(3);
214 std::string challengeStatus = statement.getString(4);
215 security::v2::Certificate cert(statement.getBlock(6));
216 std::string challengeType = statement.getString(7);
217 std::string challengeSecrets = statement.getString(8);
218 std::string challengeTp = statement.getString(9);
219 int remainingTries = statement.getInt(10);
220 int remainingTime = statement.getInt(11);
221 CertificateRequest entry(caName, requestId, status, challengeStatus, challengeType,
222 challengeTp, remainingTime, remainingTries,
223 convertString2Json(challengeSecrets), cert);
Zhiyi Zhangae123bf2017-04-14 12:24:53 -0700224 result.push_back(entry);
225 }
226 return result;
227}
228
229std::list<CertificateRequest>
230CaSqlite::listAllRequests(const Name& caName)
231{
232 std::list<CertificateRequest> result;
233 Sqlite3Statement statement(m_database,
234 R"_SQLTEXT_(SELECT * FROM CertRequests WHERE ca_name = ?)_SQLTEXT_");
235 statement.bind(1, caName.wireEncode(), SQLITE_TRANSIENT);
236
237 while(statement.step() == SQLITE_ROW) {
238 std::string requestId = statement.getString(1);
Zhiyi Zhangaf7c2902019-03-14 22:13:21 -0700239 Name caName(statement.getBlock(2));
240 int status = statement.getInt(3);
241 std::string challengeStatus = statement.getString(4);
242 security::v2::Certificate cert(statement.getBlock(6));
243 std::string challengeType = statement.getString(7);
244 std::string challengeSecrets = statement.getString(8);
245 std::string challengeTp = statement.getString(9);
246 int remainingTries = statement.getInt(10);
247 int remainingTime = statement.getInt(11);
248 CertificateRequest entry(caName, requestId, status, challengeStatus, challengeType,
249 challengeTp, remainingTime, remainingTries,
250 convertString2Json(challengeSecrets), cert);
Zhiyi Zhangae123bf2017-04-14 12:24:53 -0700251 result.push_back(entry);
252 }
253 return result;
254}
255
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700256void
257CaSqlite::deleteRequest(const std::string& requestId)
258{
259 Sqlite3Statement statement(m_database,
Zhiyi Zhangae123bf2017-04-14 12:24:53 -0700260 R"_SQLTEXT_(DELETE FROM CertRequests WHERE request_id = ?)_SQLTEXT_");
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700261 statement.bind(1, requestId, SQLITE_TRANSIENT);
262 statement.step();
263}
264
265security::v2::Certificate
266CaSqlite::getCertificate(const std::string& certId)
267{
268 Sqlite3Statement statement(m_database,
269 R"_SQLTEXT_(SELECT cert FROM IssuedCerts where cert_id = ?)_SQLTEXT_");
270 statement.bind(1, certId, SQLITE_TRANSIENT);
271
272 if (statement.step() == SQLITE_ROW) {
273 security::v2::Certificate cert(statement.getBlock(0));
274 return cert;
275 }
276 else {
277 BOOST_THROW_EXCEPTION(Error("Certificate with ID " + certId + " cannot be fetched from database"));
278 }
279}
280
281void
282CaSqlite::addCertificate(const std::string& certId, const security::v2::Certificate& cert)
283{
284 Sqlite3Statement statement(m_database,
285 R"_SQLTEXT_(INSERT INTO IssuedCerts (cert_id, cert_key_name, cert)
286 values (?, ?, ?))_SQLTEXT_");
287 statement.bind(1, certId, SQLITE_TRANSIENT);
288 statement.bind(2, cert.getKeyName().wireEncode(), SQLITE_TRANSIENT);
289 statement.bind(3, cert.wireEncode(), SQLITE_TRANSIENT);
290
291 if (statement.step() != SQLITE_DONE) {
292 BOOST_THROW_EXCEPTION(Error("Certificate " + cert.getName().toUri() + " cannot be added to database"));
293 }
294}
295
296void
297CaSqlite::updateCertificate(const std::string& certId, const security::v2::Certificate& cert)
298{
299 Sqlite3Statement statement(m_database,
300 R"_SQLTEXT_(UPDATE IssuedCerts SET cert = ? WHERE cert_id = ?)_SQLTEXT_");
301 statement.bind(1, cert.wireEncode(), SQLITE_TRANSIENT);
302 statement.bind(2, certId, SQLITE_TRANSIENT);
303
304 if (statement.step() != SQLITE_DONE) {
305 addCertificate(certId, cert);
306 }
307}
308
309void
310CaSqlite::deleteCertificate(const std::string& certId)
311{
312 Sqlite3Statement statement(m_database,
313 R"_SQLTEXT_(DELETE FROM IssuedCerts WHERE cert_id = ?)_SQLTEXT_");
314 statement.bind(1, certId, SQLITE_TRANSIENT);
315 statement.step();
316}
317
Zhiyi Zhangae123bf2017-04-14 12:24:53 -0700318std::list<security::v2::Certificate>
319CaSqlite::listAllIssuedCertificates()
320{
321 std::list<security::v2::Certificate> result;
322 Sqlite3Statement statement(m_database, R"_SQLTEXT_(SELECT * FROM IssuedCerts)_SQLTEXT_");
323
324 while (statement.step() == SQLITE_ROW) {
325 security::v2::Certificate cert(statement.getBlock(3));
326 result.push_back(cert);
327 }
328 return result;
329}
330
331std::list<security::v2::Certificate>
332CaSqlite::listAllIssuedCertificates(const Name& caName)
333{
334 auto allCerts = listAllIssuedCertificates();
335 std::list<security::v2::Certificate> result;
336 for (const auto& entry : allCerts) {
337 if (entry.getSignature().getKeyLocator().getName().getPrefix(-2) == caName) {
338 result.push_back(entry);
339 }
340 }
341 return result;
342}
343
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700344std::string
345CaSqlite::convertJson2String(const JsonSection& json)
346{
347 std::stringstream ss;
348 boost::property_tree::write_json(ss, json);
349 return ss.str();
350}
351
352JsonSection
353CaSqlite::convertString2Json(const std::string& jsonContent)
354{
355 std::istringstream ss(jsonContent);
356 JsonSection json;
357 boost::property_tree::json_parser::read_json(ss, json);
358 return json;
359}
360
361} // namespace ndncert
362} // namespace ndn