blob: 34f5d195c605f68e78d8680f4e6ab6be31a646c2 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2017, Regents of the University of California.
*
* This file is part of ndncert, a certificate management system based on NDN.
*
* ndncert is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* ndncert is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received copies of the GNU General Public License along with
* ndncert, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*
* See AUTHORS.md for complete list of ndncert authors and contributors.
*/
#include "ca-sqlite.hpp"
#include <ndn-cxx/util/sqlite3-statement.hpp>
#include <sqlite3.h>
#include <boost/filesystem.hpp>
namespace ndn {
namespace ndncert {
const std::string
CaSqlite::STORAGE_TYPE = "ca-storage-sqlite3";
NDNCERT_REGISTER_CA_STORAGE(CaSqlite);
using namespace ndn::util;
static const std::string INITIALIZATION = R"_DBTEXT_(
CREATE TABLE IF NOT EXISTS
CertRequests(
id INTEGER PRIMARY KEY,
request_id TEXT NOT NULL,
ca_name BLOB NOT NULL,
status TEXT NOT NULL,
cert_key_name BLOB NOT NULL,
cert_request BLOB NOT NULL,
challenge_type TEXT,
challenge_secrets TEXT
);
CREATE UNIQUE INDEX IF NOT EXISTS
CertRequestIdIndex ON CertRequests(request_id);
CREATE UNIQUE INDEX IF NOT EXISTS
CertRequestKeyNameIndex ON CertRequests(cert_key_name);
CREATE TABLE IF NOT EXISTS
IssuedCerts(
id INTEGER PRIMARY KEY,
cert_id TEXT NOT NULL,
cert_key_name BLOB NOT NULL,
cert BLOB NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS
IssuedCertRequestIdIndex ON IssuedCerts(cert_id);
CREATE UNIQUE INDEX IF NOT EXISTS
IssuedCertKeyNameIndex ON IssuedCerts(cert_key_name);
)_DBTEXT_";
CaSqlite::CaSqlite(const std::string& location)
: CaStorage()
{
// Determine the path of sqlite db
boost::filesystem::path dbDir;
if (!location.empty()) {
dbDir = boost::filesystem::path(location);
}
else if (getenv("HOME") != nullptr) {
dbDir = boost::filesystem::path(getenv("HOME")) / ".ndn";
}
else {
dbDir = boost::filesystem::current_path() / ".ndn";
}
boost::filesystem::create_directories(dbDir);
// open and initialize database
int result = sqlite3_open_v2((dbDir / "ndncert-ca.db").c_str(), &m_database,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
#ifdef NDN_CXX_DISABLE_SQLITE3_FS_LOCKING
"unix-dotfile"
#else
nullptr
#endif
);
if (result != SQLITE_OK)
BOOST_THROW_EXCEPTION(Error("CaSqlite DB cannot be opened/created: " + dbDir.string()));
// initialize database specific tables
char* errorMessage = nullptr;
result = sqlite3_exec(m_database, INITIALIZATION.c_str(),
nullptr, nullptr, &errorMessage);
if (result != SQLITE_OK && errorMessage != nullptr) {
sqlite3_free(errorMessage);
BOOST_THROW_EXCEPTION(Error("CaSqlite DB cannot be initialized"));
}
}
CaSqlite::~CaSqlite()
{
sqlite3_close(m_database);
}
CertificateRequest
CaSqlite::getRequest(const std::string& requestId)
{
Sqlite3Statement statement(m_database,
R"_SQLTEXT_(SELECT *
FROM CertRequests where request_id = ?)_SQLTEXT_");
statement.bind(1, requestId, SQLITE_TRANSIENT);
if (statement.step() == SQLITE_ROW) {
Name caName(statement.getBlock(2));
std::string status = statement.getString(3);
security::v2::Certificate cert(statement.getBlock(5));
std::string challengeType = statement.getString(6);
std::string challengeSecrets = statement.getString(7);
return CertificateRequest(caName, requestId, status, challengeType, challengeSecrets, cert);
}
else {
BOOST_THROW_EXCEPTION(Error("Request " + requestId + " cannot be fetched from database"));
}
}
void
CaSqlite::addRequest(const CertificateRequest& request)
{
Sqlite3Statement statement1(m_database,
R"_SQLTEXT_(SELECT * FROM CertRequests where cert_key_name = ?)_SQLTEXT_");
statement1.bind(1, request.getCert().getKeyName().wireEncode(), SQLITE_TRANSIENT);
if (statement1.step() == SQLITE_ROW) {
BOOST_THROW_EXCEPTION(Error("Request for " + request.getCert().getKeyName().toUri() + " already exists"));
return;
}
Sqlite3Statement statement2(m_database,
R"_SQLTEXT_(SELECT * FROM IssuedCerts where cert_key_name = ?)_SQLTEXT_");
statement2.bind(1, request.getCert().getKeyName().wireEncode(), SQLITE_TRANSIENT);
if (statement2.step() == SQLITE_ROW) {
BOOST_THROW_EXCEPTION(Error("Cert for " + request.getCert().getKeyName().toUri() + " already exists"));
return;
}
Sqlite3Statement statement(m_database,
R"_SQLTEXT_(INSERT INTO CertRequests (request_id, ca_name, status,
cert_key_name, cert_request, challenge_type, challenge_secrets)
values (?, ?, ?, ?, ?, ?, ?))_SQLTEXT_");
statement.bind(1, request.getRequestId(), SQLITE_TRANSIENT);
statement.bind(2, request.getCaName().wireEncode(), SQLITE_TRANSIENT);
statement.bind(3, request.getStatus(), SQLITE_TRANSIENT);
statement.bind(4, request.getCert().getKeyName().wireEncode(), SQLITE_TRANSIENT);
statement.bind(5, request.getCert().wireEncode(), SQLITE_TRANSIENT);
statement.bind(6, request.getChallengeType(), SQLITE_TRANSIENT);
statement.bind(7, convertJson2String(request.getChallengeSecrets()), SQLITE_TRANSIENT);
if (statement.step() != SQLITE_DONE) {
BOOST_THROW_EXCEPTION(Error("Request " + request.getRequestId() + " cannot be added to database"));
}
}
void
CaSqlite::updateRequest(const CertificateRequest& request)
{
Sqlite3Statement statement(m_database,
R"_SQLTEXT_(UPDATE CertRequests
SET status = ?, challenge_type = ?, challenge_secrets = ?
WHERE request_id = ?)_SQLTEXT_");
statement.bind(1, request.getStatus(), SQLITE_TRANSIENT);
statement.bind(2, request.getChallengeType(), SQLITE_TRANSIENT);
statement.bind(3, convertJson2String(request.getChallengeSecrets()), SQLITE_TRANSIENT);
statement.bind(4, request.getRequestId(), SQLITE_TRANSIENT);
if (statement.step() != SQLITE_DONE) {
addRequest(request);
}
}
std::list<CertificateRequest>
CaSqlite::listAllRequests()
{
std::list<CertificateRequest> result;
Sqlite3Statement statement(m_database, R"_SQLTEXT_(SELECT * FROM CertRequests)_SQLTEXT_");
while(statement.step() == SQLITE_ROW) {
std::string requestId = statement.getString(1);
Name caName(statement.getBlock(2));
std::string status = statement.getString(3);
security::v2::Certificate cert(statement.getBlock(5));
std::string challengeType = statement.getString(6);
std::string challengeSecrets = statement.getString(7);
CertificateRequest entry(caName, requestId, status, challengeType, challengeSecrets, cert);
result.push_back(entry);
}
return result;
}
std::list<CertificateRequest>
CaSqlite::listAllRequests(const Name& caName)
{
std::list<CertificateRequest> result;
Sqlite3Statement statement(m_database,
R"_SQLTEXT_(SELECT * FROM CertRequests WHERE ca_name = ?)_SQLTEXT_");
statement.bind(1, caName.wireEncode(), SQLITE_TRANSIENT);
while(statement.step() == SQLITE_ROW) {
std::string requestId = statement.getString(1);
std::string status = statement.getString(3);
security::v2::Certificate cert(statement.getBlock(5));
std::string challengeType = statement.getString(6);
std::string challengeSecrets = statement.getString(7);
CertificateRequest entry(caName, requestId, status, challengeType, challengeSecrets, cert);
result.push_back(entry);
}
return result;
}
void
CaSqlite::deleteRequest(const std::string& requestId)
{
Sqlite3Statement statement(m_database,
R"_SQLTEXT_(DELETE FROM CertRequests WHERE request_id = ?)_SQLTEXT_");
statement.bind(1, requestId, SQLITE_TRANSIENT);
statement.step();
}
security::v2::Certificate
CaSqlite::getCertificate(const std::string& certId)
{
Sqlite3Statement statement(m_database,
R"_SQLTEXT_(SELECT cert FROM IssuedCerts where cert_id = ?)_SQLTEXT_");
statement.bind(1, certId, SQLITE_TRANSIENT);
if (statement.step() == SQLITE_ROW) {
security::v2::Certificate cert(statement.getBlock(0));
return cert;
}
else {
BOOST_THROW_EXCEPTION(Error("Certificate with ID " + certId + " cannot be fetched from database"));
}
}
void
CaSqlite::addCertificate(const std::string& certId, const security::v2::Certificate& cert)
{
Sqlite3Statement statement(m_database,
R"_SQLTEXT_(INSERT INTO IssuedCerts (cert_id, cert_key_name, cert)
values (?, ?, ?))_SQLTEXT_");
statement.bind(1, certId, SQLITE_TRANSIENT);
statement.bind(2, cert.getKeyName().wireEncode(), SQLITE_TRANSIENT);
statement.bind(3, cert.wireEncode(), SQLITE_TRANSIENT);
if (statement.step() != SQLITE_DONE) {
BOOST_THROW_EXCEPTION(Error("Certificate " + cert.getName().toUri() + " cannot be added to database"));
}
}
void
CaSqlite::updateCertificate(const std::string& certId, const security::v2::Certificate& cert)
{
Sqlite3Statement statement(m_database,
R"_SQLTEXT_(UPDATE IssuedCerts SET cert = ? WHERE cert_id = ?)_SQLTEXT_");
statement.bind(1, cert.wireEncode(), SQLITE_TRANSIENT);
statement.bind(2, certId, SQLITE_TRANSIENT);
if (statement.step() != SQLITE_DONE) {
addCertificate(certId, cert);
}
}
void
CaSqlite::deleteCertificate(const std::string& certId)
{
Sqlite3Statement statement(m_database,
R"_SQLTEXT_(DELETE FROM IssuedCerts WHERE cert_id = ?)_SQLTEXT_");
statement.bind(1, certId, SQLITE_TRANSIENT);
statement.step();
}
std::list<security::v2::Certificate>
CaSqlite::listAllIssuedCertificates()
{
std::list<security::v2::Certificate> result;
Sqlite3Statement statement(m_database, R"_SQLTEXT_(SELECT * FROM IssuedCerts)_SQLTEXT_");
while (statement.step() == SQLITE_ROW) {
security::v2::Certificate cert(statement.getBlock(3));
result.push_back(cert);
}
return result;
}
std::list<security::v2::Certificate>
CaSqlite::listAllIssuedCertificates(const Name& caName)
{
auto allCerts = listAllIssuedCertificates();
std::list<security::v2::Certificate> result;
for (const auto& entry : allCerts) {
if (entry.getSignature().getKeyLocator().getName().getPrefix(-2) == caName) {
result.push_back(entry);
}
}
return result;
}
std::string
CaSqlite::convertJson2String(const JsonSection& json)
{
std::stringstream ss;
boost::property_tree::write_json(ss, json);
return ss.str();
}
JsonSection
CaSqlite::convertString2Json(const std::string& jsonContent)
{
std::istringstream ss(jsonContent);
JsonSection json;
boost::property_tree::json_parser::read_json(ss, json);
return json;
}
} // namespace ndncert
} // namespace ndn