blob: 34f5d195c605f68e78d8680f4e6ab6be31a646c2 [file] [log] [blame]
Zhiyi Zhang91c846b2017-04-12 14:16:31 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2017, Regents of the University of California.
4 *
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,
43 status TEXT NOT NULL,
44 cert_key_name BLOB NOT NULL,
45 cert_request BLOB NOT NULL,
46 challenge_type TEXT,
47 challenge_secrets TEXT
48 );
49CREATE UNIQUE INDEX IF NOT EXISTS
50 CertRequestIdIndex ON CertRequests(request_id);
51CREATE UNIQUE INDEX IF NOT EXISTS
52 CertRequestKeyNameIndex ON CertRequests(cert_key_name);
53
54CREATE TABLE IF NOT EXISTS
55 IssuedCerts(
56 id INTEGER PRIMARY KEY,
57 cert_id TEXT NOT NULL,
58 cert_key_name BLOB NOT NULL,
59 cert BLOB NOT NULL
60 );
61CREATE UNIQUE INDEX IF NOT EXISTS
62 IssuedCertRequestIdIndex ON IssuedCerts(cert_id);
63CREATE UNIQUE INDEX IF NOT EXISTS
64 IssuedCertKeyNameIndex ON IssuedCerts(cert_key_name);
65)_DBTEXT_";
66
67CaSqlite::CaSqlite(const std::string& location)
68 : CaStorage()
69{
70 // Determine the path of sqlite db
71 boost::filesystem::path dbDir;
72 if (!location.empty()) {
73 dbDir = boost::filesystem::path(location);
74 }
Zhiyi Zhang91c846b2017-04-12 14:16:31 -070075 else if (getenv("HOME") != nullptr) {
76 dbDir = boost::filesystem::path(getenv("HOME")) / ".ndn";
77 }
78 else {
79 dbDir = boost::filesystem::current_path() / ".ndn";
80 }
81 boost::filesystem::create_directories(dbDir);
82
83 // open and initialize database
84 int result = sqlite3_open_v2((dbDir / "ndncert-ca.db").c_str(), &m_database,
85 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
86#ifdef NDN_CXX_DISABLE_SQLITE3_FS_LOCKING
87 "unix-dotfile"
88#else
89 nullptr
90#endif
91 );
92 if (result != SQLITE_OK)
93 BOOST_THROW_EXCEPTION(Error("CaSqlite DB cannot be opened/created: " + dbDir.string()));
94
95 // initialize database specific tables
96 char* errorMessage = nullptr;
97 result = sqlite3_exec(m_database, INITIALIZATION.c_str(),
98 nullptr, nullptr, &errorMessage);
99 if (result != SQLITE_OK && errorMessage != nullptr) {
100 sqlite3_free(errorMessage);
101 BOOST_THROW_EXCEPTION(Error("CaSqlite DB cannot be initialized"));
102 }
103}
104
105CaSqlite::~CaSqlite()
106{
107 sqlite3_close(m_database);
108}
109
110CertificateRequest
111CaSqlite::getRequest(const std::string& requestId)
112{
113 Sqlite3Statement statement(m_database,
114 R"_SQLTEXT_(SELECT *
115 FROM CertRequests where request_id = ?)_SQLTEXT_");
116 statement.bind(1, requestId, SQLITE_TRANSIENT);
117
118 if (statement.step() == SQLITE_ROW) {
119 Name caName(statement.getBlock(2));
120 std::string status = statement.getString(3);
121 security::v2::Certificate cert(statement.getBlock(5));
122 std::string challengeType = statement.getString(6);
123 std::string challengeSecrets = statement.getString(7);
124 return CertificateRequest(caName, requestId, status, challengeType, challengeSecrets, cert);
125 }
126 else {
127 BOOST_THROW_EXCEPTION(Error("Request " + requestId + " cannot be fetched from database"));
128 }
129}
130
131void
132CaSqlite::addRequest(const CertificateRequest& request)
133{
134 Sqlite3Statement statement1(m_database,
135 R"_SQLTEXT_(SELECT * FROM CertRequests where cert_key_name = ?)_SQLTEXT_");
136 statement1.bind(1, request.getCert().getKeyName().wireEncode(), SQLITE_TRANSIENT);
137 if (statement1.step() == SQLITE_ROW) {
138 BOOST_THROW_EXCEPTION(Error("Request for " + request.getCert().getKeyName().toUri() + " already exists"));
139 return;
140 }
141
142 Sqlite3Statement statement2(m_database,
143 R"_SQLTEXT_(SELECT * FROM IssuedCerts where cert_key_name = ?)_SQLTEXT_");
144 statement2.bind(1, request.getCert().getKeyName().wireEncode(), SQLITE_TRANSIENT);
145 if (statement2.step() == SQLITE_ROW) {
146 BOOST_THROW_EXCEPTION(Error("Cert for " + request.getCert().getKeyName().toUri() + " already exists"));
147 return;
148 }
149
150 Sqlite3Statement statement(m_database,
151 R"_SQLTEXT_(INSERT INTO CertRequests (request_id, ca_name, status,
152 cert_key_name, cert_request, challenge_type, challenge_secrets)
153 values (?, ?, ?, ?, ?, ?, ?))_SQLTEXT_");
154 statement.bind(1, request.getRequestId(), SQLITE_TRANSIENT);
155 statement.bind(2, request.getCaName().wireEncode(), SQLITE_TRANSIENT);
156 statement.bind(3, request.getStatus(), SQLITE_TRANSIENT);
157 statement.bind(4, request.getCert().getKeyName().wireEncode(), SQLITE_TRANSIENT);
158 statement.bind(5, request.getCert().wireEncode(), SQLITE_TRANSIENT);
159 statement.bind(6, request.getChallengeType(), SQLITE_TRANSIENT);
160 statement.bind(7, convertJson2String(request.getChallengeSecrets()), SQLITE_TRANSIENT);
161
162 if (statement.step() != SQLITE_DONE) {
163 BOOST_THROW_EXCEPTION(Error("Request " + request.getRequestId() + " cannot be added to database"));
164 }
165}
166
167void
168CaSqlite::updateRequest(const CertificateRequest& request)
169{
170 Sqlite3Statement statement(m_database,
171 R"_SQLTEXT_(UPDATE CertRequests
172 SET status = ?, challenge_type = ?, challenge_secrets = ?
173 WHERE request_id = ?)_SQLTEXT_");
174 statement.bind(1, request.getStatus(), SQLITE_TRANSIENT);
175 statement.bind(2, request.getChallengeType(), SQLITE_TRANSIENT);
176 statement.bind(3, convertJson2String(request.getChallengeSecrets()), SQLITE_TRANSIENT);
177 statement.bind(4, request.getRequestId(), SQLITE_TRANSIENT);
178
179 if (statement.step() != SQLITE_DONE) {
180 addRequest(request);
181 }
182}
183
Zhiyi Zhangae123bf2017-04-14 12:24:53 -0700184std::list<CertificateRequest>
185CaSqlite::listAllRequests()
186{
187 std::list<CertificateRequest> result;
188 Sqlite3Statement statement(m_database, R"_SQLTEXT_(SELECT * FROM CertRequests)_SQLTEXT_");
189
190 while(statement.step() == SQLITE_ROW) {
191 std::string requestId = statement.getString(1);
192 Name caName(statement.getBlock(2));
193 std::string status = statement.getString(3);
194 security::v2::Certificate cert(statement.getBlock(5));
195 std::string challengeType = statement.getString(6);
196 std::string challengeSecrets = statement.getString(7);
197 CertificateRequest entry(caName, requestId, status, challengeType, challengeSecrets, cert);
198 result.push_back(entry);
199 }
200 return result;
201}
202
203std::list<CertificateRequest>
204CaSqlite::listAllRequests(const Name& caName)
205{
206 std::list<CertificateRequest> result;
207 Sqlite3Statement statement(m_database,
208 R"_SQLTEXT_(SELECT * FROM CertRequests WHERE ca_name = ?)_SQLTEXT_");
209 statement.bind(1, caName.wireEncode(), SQLITE_TRANSIENT);
210
211 while(statement.step() == SQLITE_ROW) {
212 std::string requestId = statement.getString(1);
213 std::string status = statement.getString(3);
214 security::v2::Certificate cert(statement.getBlock(5));
215 std::string challengeType = statement.getString(6);
216 std::string challengeSecrets = statement.getString(7);
217 CertificateRequest entry(caName, requestId, status, challengeType, challengeSecrets, cert);
218 result.push_back(entry);
219 }
220 return result;
221}
222
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700223void
224CaSqlite::deleteRequest(const std::string& requestId)
225{
226 Sqlite3Statement statement(m_database,
Zhiyi Zhangae123bf2017-04-14 12:24:53 -0700227 R"_SQLTEXT_(DELETE FROM CertRequests WHERE request_id = ?)_SQLTEXT_");
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700228 statement.bind(1, requestId, SQLITE_TRANSIENT);
229 statement.step();
230}
231
232security::v2::Certificate
233CaSqlite::getCertificate(const std::string& certId)
234{
235 Sqlite3Statement statement(m_database,
236 R"_SQLTEXT_(SELECT cert FROM IssuedCerts where cert_id = ?)_SQLTEXT_");
237 statement.bind(1, certId, SQLITE_TRANSIENT);
238
239 if (statement.step() == SQLITE_ROW) {
240 security::v2::Certificate cert(statement.getBlock(0));
241 return cert;
242 }
243 else {
244 BOOST_THROW_EXCEPTION(Error("Certificate with ID " + certId + " cannot be fetched from database"));
245 }
246}
247
248void
249CaSqlite::addCertificate(const std::string& certId, const security::v2::Certificate& cert)
250{
251 Sqlite3Statement statement(m_database,
252 R"_SQLTEXT_(INSERT INTO IssuedCerts (cert_id, cert_key_name, cert)
253 values (?, ?, ?))_SQLTEXT_");
254 statement.bind(1, certId, SQLITE_TRANSIENT);
255 statement.bind(2, cert.getKeyName().wireEncode(), SQLITE_TRANSIENT);
256 statement.bind(3, cert.wireEncode(), SQLITE_TRANSIENT);
257
258 if (statement.step() != SQLITE_DONE) {
259 BOOST_THROW_EXCEPTION(Error("Certificate " + cert.getName().toUri() + " cannot be added to database"));
260 }
261}
262
263void
264CaSqlite::updateCertificate(const std::string& certId, const security::v2::Certificate& cert)
265{
266 Sqlite3Statement statement(m_database,
267 R"_SQLTEXT_(UPDATE IssuedCerts SET cert = ? WHERE cert_id = ?)_SQLTEXT_");
268 statement.bind(1, cert.wireEncode(), SQLITE_TRANSIENT);
269 statement.bind(2, certId, SQLITE_TRANSIENT);
270
271 if (statement.step() != SQLITE_DONE) {
272 addCertificate(certId, cert);
273 }
274}
275
276void
277CaSqlite::deleteCertificate(const std::string& certId)
278{
279 Sqlite3Statement statement(m_database,
280 R"_SQLTEXT_(DELETE FROM IssuedCerts WHERE cert_id = ?)_SQLTEXT_");
281 statement.bind(1, certId, SQLITE_TRANSIENT);
282 statement.step();
283}
284
Zhiyi Zhangae123bf2017-04-14 12:24:53 -0700285std::list<security::v2::Certificate>
286CaSqlite::listAllIssuedCertificates()
287{
288 std::list<security::v2::Certificate> result;
289 Sqlite3Statement statement(m_database, R"_SQLTEXT_(SELECT * FROM IssuedCerts)_SQLTEXT_");
290
291 while (statement.step() == SQLITE_ROW) {
292 security::v2::Certificate cert(statement.getBlock(3));
293 result.push_back(cert);
294 }
295 return result;
296}
297
298std::list<security::v2::Certificate>
299CaSqlite::listAllIssuedCertificates(const Name& caName)
300{
301 auto allCerts = listAllIssuedCertificates();
302 std::list<security::v2::Certificate> result;
303 for (const auto& entry : allCerts) {
304 if (entry.getSignature().getKeyLocator().getName().getPrefix(-2) == caName) {
305 result.push_back(entry);
306 }
307 }
308 return result;
309}
310
Zhiyi Zhang91c846b2017-04-12 14:16:31 -0700311std::string
312CaSqlite::convertJson2String(const JsonSection& json)
313{
314 std::stringstream ss;
315 boost::property_tree::write_json(ss, json);
316 return ss.str();
317}
318
319JsonSection
320CaSqlite::convertString2Json(const std::string& jsonContent)
321{
322 std::istringstream ss(jsonContent);
323 JsonSection json;
324 boost::property_tree::json_parser::read_json(ss, json);
325 return json;
326}
327
328} // namespace ndncert
329} // namespace ndn