From 25b4018f92fc3245ad91ff6c0d12fc5dacc3cadd Mon Sep 17 00:00:00 2001 From: =?utf8?q?Felix=20D=C3=B6rre?= Date: Sat, 8 Apr 2017 22:26:03 +0200 Subject: [PATCH] add: signing of OCSP certificates Change-Id: If9298ce309986604d53e82f9b3bc16536dfc70f0 --- docs/Protocol.md | 2 + src/apps/client.cpp | 86 ++++++++++++++++++++++++++++++ src/config.cpp | 10 ++-- src/crypto/remoteSigner.cpp | 8 ++- src/crypto/simpleOpensslSigner.cpp | 35 ++++++++---- src/db/database.h | 2 + src/io/record.h | 1 + src/io/recordHandler.cpp | 4 ++ 8 files changed, 135 insertions(+), 13 deletions(-) diff --git a/docs/Protocol.md b/docs/Protocol.md index 590658c..5379a07 100644 --- a/docs/Protocol.md +++ b/docs/Protocol.md @@ -44,6 +44,8 @@ Record Types/Commands Sets the desired starting date. (0x13) wishTo Sets the desired ending date (or validity-period). +(0x14) ocspTargetCA + Used instead of (0x11) when signing OCSP certs. The payload is an exact time-CA name (e.g. orga_2017_1). (0x18) addSAN Adds a given SAN (Subject alternative name) to the certificate. (DNS, or email,) diff --git a/src/apps/client.cpp b/src/apps/client.cpp index 92a2369..63ff0ca 100644 --- a/src/apps/client.cpp +++ b/src/apps/client.cpp @@ -17,6 +17,8 @@ #include "io/slipBio.h" #include "config.h" #include +#include +#include #ifdef NO_DAEMON #define DAEMON false @@ -52,6 +54,88 @@ void checkCRLs( std::shared_ptr sign ) { } } +bool pathExists( const std::string& name ) { + struct stat buffer; + return stat( name.c_str(), &buffer ) == 0; +} + +void checkOCSP( std::shared_ptr sign ) { + std::unique_ptr> dp( opendir( "ca" ), []( DIR * d ) { + closedir( d ); + } ); + + // When opendir fails and returns 0 the unique_ptr will be considered unintialized and will not call closedir. + // Even if closedir would be called, according to POSIX it MAY handle nullptr properly (for glibc it does). + if( !dp ) { + logger::error( "CA directory not found" ); + return; + } + + struct dirent *ep; + + while( ( ep = readdir( dp.get() ) ) ) { + if( ep->d_name[0] == '.' ) { + continue; + } + + std::string profileName( ep->d_name ); + std::string csr = "ca/" + profileName + "/ocsp.csr"; + + if( ! pathExists( csr ) ) { + continue; + } + + std::string crtName = "ca/" + profileName + "/ocsp.crt"; + + if( pathExists( crtName ) ) { + continue; + } + + logger::notef( "Discovered OCSP CSR that needs action: %s", csr ); + std::string req = readFile( csr ); + std::shared_ptr parsed = X509Req::parseCSR( req ); + + if( parsed->verify() <= 0 ) { + logger::errorf( "Invalid CSR for %s", profileName ); + continue; + } + + auto cert = std::make_shared(); + cert->ocspCA = profileName; + cert->wishFrom = "now"; + cert->wishTo = "1y"; + cert->md = "sha512"; + + logger::note( "INFO: Message Digest: ", cert->md ); + + for( auto& SAN : cert->SANs ) { + logger::notef( "INFO: SAN %s: %s", SAN->type, SAN->content ); + } + + for( auto& AVA : cert->AVAs ) { + logger::notef( "INFO: AVA %s: %s", AVA->name, AVA->value ); + } + + cert->csr_content = req; + cert->csr_type = "CSR"; + auto nAVA = std::make_shared(); + nAVA->name = "CN"; + nAVA->value = "OCSP Responder"; + cert->AVAs.push_back( nAVA ); + + std::shared_ptr res = sign->sign( cert ); + + if( !res ) { + logger::error( "OCSP Cert signing failed." ); + continue; + } + + writeFile( crtName, res->certificate ); + logger::notef( "Cert log: %s", res->log ); + } +} + + int main( int argc, const char *argv[] ) { bool once = false; bool resetOnly = false; @@ -114,6 +198,8 @@ int main( int argc, const char *argv[] ) { lastCRLCheck = current; } + checkOCSP( sign ); + std::shared_ptr job; try { diff --git a/src/config.cpp b/src/config.cpp index d078179..a58cc82 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -93,16 +93,20 @@ int parseProfiles() { prof.ku = map->at( "ku" ); prof.maxValidity = std::stoi( map->at( "days" ) ) * /* DAYS */24 * 60 * 60; - std::string cas = map->at( "ca" ); DIR *dir; struct dirent *ent; - if( ( dir = opendir( "ca" ) ) != NULL ) { + if( profileName == "0100-ocsp" ) { + //This profile does not have a specific CA. The concrete CA has to be set in each request. + } else if( ( dir = opendir( "ca" ) ) != NULL ) { + std::string cas = map->at( "ca" ); + std::string toFind = cas + "_"; + while( ( ent = readdir( dir ) ) != NULL ) { std::string caName = std::string( ent->d_name ); - if( caName.find( cas ) != 0 ) { + if( caName.find( toFind ) != 0 ) { continue; } diff --git a/src/crypto/remoteSigner.cpp b/src/crypto/remoteSigner.cpp index 64ccebf..c06af91 100644 --- a/src/crypto/remoteSigner.cpp +++ b/src/crypto/remoteSigner.cpp @@ -44,7 +44,13 @@ std::shared_ptr RemoteSigner::sign( std::shared_ptrmd ); - send( conn, head, RecordHeader::SignerCommand::SET_PROFILE, cert->profile ); + + if( !cert->ocspCA.empty() ) { + send( conn, head, RecordHeader::SignerCommand::SET_OCSP_TARGET_CA, cert->ocspCA ); + } else { + send( conn, head, RecordHeader::SignerCommand::SET_PROFILE, cert->profile ); + } + send( conn, head, RecordHeader::SignerCommand::SET_WISH_FROM, cert->wishFrom ); send( conn, head, RecordHeader::SignerCommand::SET_WISH_TO, cert->wishTo ); diff --git a/src/crypto/simpleOpensslSigner.cpp b/src/crypto/simpleOpensslSigner.cpp index 6cf10d0..a6d0371 100644 --- a/src/crypto/simpleOpensslSigner.cpp +++ b/src/crypto/simpleOpensslSigner.cpp @@ -18,6 +18,7 @@ #include "sslUtil.h" extern std::unordered_map profiles; +extern std::unordered_map> CAs; std::shared_ptr SimpleOpensslSigner::lib_ref = ssl_lib_ref; @@ -74,12 +75,28 @@ std::shared_ptr SimpleOpensslSigner::sign( std::shared_ptrprofile ); + std::shared_ptr ca; + Profile *prof; - Profile& prof = profiles.at( cert->profile ); - logger::note( "FINE: Profile ID is: ", prof.id ); + if( cert->ocspCA != "" ) { + auto caIterator = CAs.find( cert->ocspCA ); - std::shared_ptr ca = prof.getCA(); + if( caIterator == CAs.end() ) { + logger::error( "ERROR: Signing CA specified in request for an OCSP cert could not be loaded." ); + throw std::runtime_error( "CA-key for OCSP cert not found" ); + } + + ca = caIterator->second; + logger::note( "Trying to fetch OCSP-profile" ); + prof = &profiles.at( "0100-ocsp" ); + logger::note( "Done with it" ); + } else { + logger::note( "FINE: Profile name is: ", cert->profile ); + + prof = &profiles.at( cert->profile ); + logger::note( "FINE: Profile ID is: ", prof->id ); + ca = prof->getCA(); + } if( !ca ) { logger::error( "ERROR: Signing CA specified in profile could not be loaded." ); @@ -92,8 +109,8 @@ std::shared_ptr SimpleOpensslSigner::sign( std::shared_ptrku ); + logger::note( "INFO: Extended Key Usage is: ", prof->eku ); logger::note( "FINE: Signing is wanted by: ", cert->wishFrom ); logger::note( "FINE: Signing is wanted for: ", cert->wishTo ); @@ -207,7 +224,7 @@ std::shared_ptr SimpleOpensslSigner::sign( std::shared_ptrmaxValidity; if( ( to - from > limit ) || ( to - from < 0 ) ) { to = from + limit; @@ -232,13 +249,13 @@ std::shared_ptr SimpleOpensslSigner::sign( std::shared_ptrca, cert->SANs, prof, ca->crlURL, ca->crtURL ); + c.setExtensions( ca->ca, cert->SANs, *prof, ca->crlURL, ca->crtURL ); logger::note( "FINE: Setting extensions successful." ); logger::note( "INFO: Generating next Serial Number ..." ); std::shared_ptr ser; std::string num; - std::tie( ser, num ) = nextSerial( prof, ca ); + std::tie( ser, num ) = nextSerial( *prof, ca ); c.setSerialNumber( ser.get() ); logger::note( "FINE: Certificate Serial Number set to: ", num ); diff --git a/src/db/database.h b/src/db/database.h index 31687b1..fb3b093 100644 --- a/src/db/database.h +++ b/src/db/database.h @@ -38,6 +38,8 @@ struct TBSCertificate { std::string wishFrom; std::string wishTo; + + std::string ocspCA; }; struct SignedCertificate { diff --git a/src/io/record.h b/src/io/record.h index 5b9c440..6c2df65 100644 --- a/src/io/record.h +++ b/src/io/record.h @@ -19,6 +19,7 @@ public: SET_PROFILE = 0x11, SET_WISH_FROM = 0x12, SET_WISH_TO = 0x13, + SET_OCSP_TARGET_CA = 0x14, ADD_SAN = 0x18, ADD_AVA = 0x19, ADD_PROOF_LINE = 0x40, diff --git a/src/io/recordHandler.cpp b/src/io/recordHandler.cpp index 7b48c6e..11639c2 100644 --- a/src/io/recordHandler.cpp +++ b/src/io/recordHandler.cpp @@ -112,6 +112,10 @@ public: tbs->wishTo = data; break; + case RecordHeader::SignerCommand::SET_OCSP_TARGET_CA: + tbs->ocspCA = data; + break; + case RecordHeader::SignerCommand::ADD_SAN: { size_t pos = data.find( "," ); -- 2.39.2