blob: 46249dc65fcd4589eaebffc1fce95384f3f36c50 [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 }
75#ifdef HAVE_TESTS
76 else if (getenv("TEST_HOME") != nullptr) {
77 dbDir = boost::filesystem::path(getenv("TEST_HOME")) / ".ndn";
78 }
79#endif // HAVE_TESTS
80 else if (getenv("HOME") != nullptr) {
81 dbDir = boost::filesystem::path(getenv("HOME")) / ".ndn";
82 }
83 else {
84 dbDir = boost::filesystem::current_path() / ".ndn";
85 }
86 boost::filesystem::create_directories(dbDir);
87
88 // open and initialize database
89 int result = sqlite3_open_v2((dbDir / "ndncert-ca.db").c_str(), &m_database,
90 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
91#ifdef NDN_CXX_DISABLE_SQLITE3_FS_LOCKING
92 "unix-dotfile"
93#else
94 nullptr
95#endif
96 );
97 if (result != SQLITE_OK)
98 BOOST_THROW_EXCEPTION(Error("CaSqlite DB cannot be opened/created: " + dbDir.string()));
99
100 // initialize database specific tables
101 char* errorMessage = nullptr;
102 result = sqlite3_exec(m_database, INITIALIZATION.c_str(),
103 nullptr, nullptr, &errorMessage);
104 if (result != SQLITE_OK && errorMessage != nullptr) {
105 sqlite3_free(errorMessage);
106 BOOST_THROW_EXCEPTION(Error("CaSqlite DB cannot be initialized"));
107 }
108}
109
110CaSqlite::~CaSqlite()
111{
112 sqlite3_close(m_database);
113}
114
115CertificateRequest
116CaSqlite::getRequest(const std::string& requestId)
117{
118 Sqlite3Statement statement(m_database,
119 R"_SQLTEXT_(SELECT *
120 FROM CertRequests where request_id = ?)_SQLTEXT_");
121 statement.bind(1, requestId, SQLITE_TRANSIENT);
122
123 if (statement.step() == SQLITE_ROW) {
124 Name caName(statement.getBlock(2));
125 std::string status = statement.getString(3);
126 security::v2::Certificate cert(statement.getBlock(5));
127 std::string challengeType = statement.getString(6);
128 std::string challengeSecrets = statement.getString(7);
129 return CertificateRequest(caName, requestId, status, challengeType, challengeSecrets, cert);
130 }
131 else {
132 BOOST_THROW_EXCEPTION(Error("Request " + requestId + " cannot be fetched from database"));
133 }
134}
135
136void
137CaSqlite::addRequest(const CertificateRequest& request)
138{
139 Sqlite3Statement statement1(m_database,
140 R"_SQLTEXT_(SELECT * FROM CertRequests where cert_key_name = ?)_SQLTEXT_");
141 statement1.bind(1, request.getCert().getKeyName().wireEncode(), SQLITE_TRANSIENT);
142 if (statement1.step() == SQLITE_ROW) {
143 BOOST_THROW_EXCEPTION(Error("Request for " + request.getCert().getKeyName().toUri() + " already exists"));
144 return;
145 }
146
147 Sqlite3Statement statement2(m_database,
148 R"_SQLTEXT_(SELECT * FROM IssuedCerts where cert_key_name = ?)_SQLTEXT_");
149 statement2.bind(1, request.getCert().getKeyName().wireEncode(), SQLITE_TRANSIENT);
150 if (statement2.step() == SQLITE_ROW) {
151 BOOST_THROW_EXCEPTION(Error("Cert for " + request.getCert().getKeyName().toUri() + " already exists"));
152 return;
153 }
154
155 Sqlite3Statement statement(m_database,
156 R"_SQLTEXT_(INSERT INTO CertRequests (request_id, ca_name, status,
157 cert_key_name, cert_request, challenge_type, challenge_secrets)
158 values (?, ?, ?, ?, ?, ?, ?))_SQLTEXT_");
159 statement.bind(1, request.getRequestId(), SQLITE_TRANSIENT);
160 statement.bind(2, request.getCaName().wireEncode(), SQLITE_TRANSIENT);
161 statement.bind(3, request.getStatus(), SQLITE_TRANSIENT);
162 statement.bind(4, request.getCert().getKeyName().wireEncode(), SQLITE_TRANSIENT);
163 statement.bind(5, request.getCert().wireEncode(), SQLITE_TRANSIENT);
164 statement.bind(6, request.getChallengeType(), SQLITE_TRANSIENT);
165 statement.bind(7, convertJson2String(request.getChallengeSecrets()), SQLITE_TRANSIENT);
166
167 if (statement.step() != SQLITE_DONE) {
168 BOOST_THROW_EXCEPTION(Error("Request " + request.getRequestId() + " cannot be added to database"));
169 }
170}
171
172void
173CaSqlite::updateRequest(const CertificateRequest& request)
174{
175 Sqlite3Statement statement(m_database,
176 R"_SQLTEXT_(UPDATE CertRequests
177 SET status = ?, challenge_type = ?, challenge_secrets = ?
178 WHERE request_id = ?)_SQLTEXT_");
179 statement.bind(1, request.getStatus(), SQLITE_TRANSIENT);
180 statement.bind(2, request.getChallengeType(), SQLITE_TRANSIENT);
181 statement.bind(3, convertJson2String(request.getChallengeSecrets()), SQLITE_TRANSIENT);
182 statement.bind(4, request.getRequestId(), SQLITE_TRANSIENT);
183
184 if (statement.step() != SQLITE_DONE) {
185 addRequest(request);
186 }
187}
188
189void
190CaSqlite::deleteRequest(const std::string& requestId)
191{
192 Sqlite3Statement statement(m_database,
193 R"_SQLTEXT_(DELETE FROM CertRequest WHERE request_id = ?)_SQLTEXT_");
194 statement.bind(1, requestId, SQLITE_TRANSIENT);
195 statement.step();
196}
197
198security::v2::Certificate
199CaSqlite::getCertificate(const std::string& certId)
200{
201 Sqlite3Statement statement(m_database,
202 R"_SQLTEXT_(SELECT cert FROM IssuedCerts where cert_id = ?)_SQLTEXT_");
203 statement.bind(1, certId, SQLITE_TRANSIENT);
204
205 if (statement.step() == SQLITE_ROW) {
206 security::v2::Certificate cert(statement.getBlock(0));
207 return cert;
208 }
209 else {
210 BOOST_THROW_EXCEPTION(Error("Certificate with ID " + certId + " cannot be fetched from database"));
211 }
212}
213
214void
215CaSqlite::addCertificate(const std::string& certId, const security::v2::Certificate& cert)
216{
217 Sqlite3Statement statement(m_database,
218 R"_SQLTEXT_(INSERT INTO IssuedCerts (cert_id, cert_key_name, cert)
219 values (?, ?, ?))_SQLTEXT_");
220 statement.bind(1, certId, SQLITE_TRANSIENT);
221 statement.bind(2, cert.getKeyName().wireEncode(), SQLITE_TRANSIENT);
222 statement.bind(3, cert.wireEncode(), SQLITE_TRANSIENT);
223
224 if (statement.step() != SQLITE_DONE) {
225 BOOST_THROW_EXCEPTION(Error("Certificate " + cert.getName().toUri() + " cannot be added to database"));
226 }
227}
228
229void
230CaSqlite::updateCertificate(const std::string& certId, const security::v2::Certificate& cert)
231{
232 Sqlite3Statement statement(m_database,
233 R"_SQLTEXT_(UPDATE IssuedCerts SET cert = ? WHERE cert_id = ?)_SQLTEXT_");
234 statement.bind(1, cert.wireEncode(), SQLITE_TRANSIENT);
235 statement.bind(2, certId, SQLITE_TRANSIENT);
236
237 if (statement.step() != SQLITE_DONE) {
238 addCertificate(certId, cert);
239 }
240}
241
242void
243CaSqlite::deleteCertificate(const std::string& certId)
244{
245 Sqlite3Statement statement(m_database,
246 R"_SQLTEXT_(DELETE FROM IssuedCerts WHERE cert_id = ?)_SQLTEXT_");
247 statement.bind(1, certId, SQLITE_TRANSIENT);
248 statement.step();
249}
250
251std::string
252CaSqlite::convertJson2String(const JsonSection& json)
253{
254 std::stringstream ss;
255 boost::property_tree::write_json(ss, json);
256 return ss.str();
257}
258
259JsonSection
260CaSqlite::convertString2Json(const std::string& jsonContent)
261{
262 std::istringstream ss(jsonContent);
263 JsonSection json;
264 boost::property_tree::json_parser::read_json(ss, json);
265 return json;
266}
267
268} // namespace ndncert
269} // namespace ndn