Merge branch 'ocsp-responder-index'

Adds support for multiple OCSP responders in `pki --ocsp` and one that
is based on OpenSSL-style index.txt files.  The parser for these files
also accepts simplified files that only specify the status, serial number
and optional revocation date/reason.  The OCSP test scenarios are also
updated to use this OCSP responder including one that shows the multi-CA
capabilities of the --ocsp command and the --index option.
This commit is contained in:
Tobias Brunner 2023-11-13 12:51:47 +01:00
commit a1224b6c80
27 changed files with 688 additions and 79 deletions

View File

@ -28,7 +28,7 @@ credentials/certificates/ocsp_response.c credentials/certificates/x509.c \
credentials/certificates/ocsp_single_response.c \
credentials/certificates/certificate_printer.c \
credentials/containers/container.c credentials/containers/pkcs12.c \
credentials/credential_manager.c \
credentials/credential_manager.c credentials/ocsp_responders.c \
credentials/sets/auth_cfg_wrapper.c credentials/sets/ocsp_response_wrapper.c \
credentials/sets/cert_cache.c credentials/sets/mem_cred.c \
credentials/sets/callback_cred.c credentials/auth_cfg.c database/database.c \

View File

@ -26,7 +26,7 @@ credentials/certificates/ocsp_response.c credentials/certificates/x509.c \
credentials/certificates/ocsp_single_response.c \
credentials/certificates/certificate_printer.c \
credentials/containers/container.c credentials/containers/pkcs12.c \
credentials/credential_manager.c \
credentials/credential_manager.c credentials/ocsp_responders.c \
credentials/sets/auth_cfg_wrapper.c credentials/sets/ocsp_response_wrapper.c \
credentials/sets/cert_cache.c credentials/sets/mem_cred.c \
credentials/sets/callback_cred.c credentials/auth_cfg.c database/database.c \
@ -99,10 +99,11 @@ credentials/certificates/pgp_certificate.h \
credentials/certificates/certificate_printer.h \
credentials/containers/container.h credentials/containers/pkcs7.h \
credentials/containers/pkcs12.h \
credentials/credential_manager.h credentials/sets/auth_cfg_wrapper.h \
credentials/sets/ocsp_response_wrapper.h credentials/sets/cert_cache.h \
credentials/sets/mem_cred.h credentials/sets/callback_cred.h \
credentials/auth_cfg.h credentials/credential_set.h credentials/cert_validator.h \
credentials/credential_manager.h credentials/ocsp_responders.h \
credentials/sets/auth_cfg_wrapper.h credentials/sets/ocsp_response_wrapper.h \
credentials/sets/cert_cache.h credentials/sets/mem_cred.h \
credentials/sets/callback_cred.h credentials/auth_cfg.h \
credentials/credential_set.h credentials/cert_validator.h \
database/database.h database/database_factory.h fetcher/fetcher.h \
fetcher/fetcher_manager.h eap/eap.h pen/pen.h ipsec/ipsec_types.h \
metadata/metadata.h metadata/metadata_factory.h metadata/metadata_int.h \

View File

@ -22,10 +22,10 @@
#ifndef OCSP_RESPONDER_H_
#define OCSP_RESPONDER_H_
#include <credentials/certificates/crl.h>
typedef struct ocsp_responder_t ocsp_responder_t;
#include <credentials/certificates/crl.h>
/**
* OCSP responder object.
*/
@ -34,6 +34,8 @@ struct ocsp_responder_t {
/**
* Check the status of a certificate given by its serial number
*
* @note Return VALIDATION_SKIPPED if not responsible for the given CA
*
* @param cacert X.509 certificate of issuer CA
* @param serial_number serial number of the certificate to be checked
* @param revocation_time receives time of revocation, if revoked

View File

@ -0,0 +1,119 @@
/*
* Copyright (C) 2023 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
* This program 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 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program 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.
*/
#include "ocsp_responders.h"
#include <collections/linked_list.h>
#include <threading/rwlock.h>
typedef struct private_ocsp_responders_t private_ocsp_responders_t;
/**
* Private data
*/
struct private_ocsp_responders_t {
/**
* Public interface
*/
ocsp_responders_t public;
/**
* List of registered OCSP responders
*/
linked_list_t *responders;
/**
* Lock to access responder list
*/
rwlock_t *lock;
};
METHOD(ocsp_responders_t, get_status, cert_validation_t,
private_ocsp_responders_t *this, certificate_t *cacert,
chunk_t serial_number, time_t *revocation_time,
crl_reason_t *revocation_reason)
{
enumerator_t *enumerator;
ocsp_responder_t *current;
cert_validation_t validation = VALIDATION_SKIPPED;
this->lock->read_lock(this->lock);
enumerator = this->responders->create_enumerator(this->responders);
while (enumerator->enumerate(enumerator, &current))
{
validation = current->get_status(current, cacert, serial_number,
revocation_time, revocation_reason);
if (validation != VALIDATION_SKIPPED &&
validation != VALIDATION_FAILED)
{
break;
}
}
enumerator->destroy(enumerator);
this->lock->unlock(this->lock);
if (validation == VALIDATION_SKIPPED)
{
validation = VALIDATION_FAILED;
}
return validation;
}
METHOD(ocsp_responders_t, add_responder, void,
private_ocsp_responders_t *this, ocsp_responder_t *responder)
{
this->lock->write_lock(this->lock);
this->responders->insert_last(this->responders, responder);
this->lock->unlock(this->lock);
}
METHOD(ocsp_responders_t, remove_responder, void,
private_ocsp_responders_t *this, ocsp_responder_t *responder)
{
this->lock->write_lock(this->lock);
this->responders->remove(this->responders, responder, NULL);
this->lock->unlock(this->lock);
}
METHOD(ocsp_responders_t, destroy, void,
private_ocsp_responders_t *this)
{
this->responders->destroy(this->responders);
this->lock->destroy(this->lock);
free(this);
}
/*
* Described in header
*/
ocsp_responders_t *ocsp_responders_create()
{
private_ocsp_responders_t *this;
INIT(this,
.public = {
.get_status = _get_status,
.add_responder = _add_responder,
.remove_responder = _remove_responder,
.destroy = _destroy,
},
.responders = linked_list_create(),
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
);
return &this->public;
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2023 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
* This program 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 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program 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.
*/
/**
* @defgroup ocsp_responders ocsp_responders
* @{ @ingroup credentials
*/
#ifndef OCSP_RESPONDERS_H_
#define OCSP_RESPONDERS_H_
typedef struct ocsp_responders_t ocsp_responders_t;
#include <credentials/certificates/ocsp_responder.h>
/**
* Manages OCSP responders.
*/
struct ocsp_responders_t {
/**
* Check the status of a certificate given by its serial number
*
* @param cacert X.509 certificate of issuer CA
* @param serial_number serial number of the certificate to be checked
* @param revocation_time receives time of revocation, if revoked
* @param reason receives reason of revocation, if revoked
* @return certificate validation status
*/
cert_validation_t (*get_status)(ocsp_responders_t *this,
certificate_t *cacert,
chunk_t serial_number,
time_t *revocation_time,
crl_reason_t *revocation_reason);
/**
* Register an OCSP responder with this manager.
*
* @param responder OCSP responder to register
*/
void (*add_responder)(ocsp_responders_t *this,
ocsp_responder_t *responder);
/**
* Unregister an OCSP responder from this manager.
*
* @param responder OCSP responder to unregister
*/
void (*remove_responder)(ocsp_responders_t *this,
ocsp_responder_t *responder);
/**
* Destroy a ocsp_responders_t instance.
*/
void (*destroy)(ocsp_responders_t *this);
};
/**
* Create a ocsp_responders_t instance.
*/
ocsp_responders_t *ocsp_responders_create();
#endif /** OCSP_RESPONDERS_H_ @}*/

View File

@ -171,6 +171,7 @@ void library_deinit()
this->public.credmgr->destroy(this->public.credmgr);
this->public.creds->destroy(this->public.creds);
this->public.encoding->destroy(this->public.encoding);
this->public.ocsp->destroy(this->public.ocsp);
this->public.metadata->destroy(this->public.metadata);
this->public.crypto->destroy(this->public.crypto);
this->public.caps->destroy(this->public.caps);
@ -401,6 +402,7 @@ bool library_init(char *settings, const char *namespace)
this->public.creds = credential_factory_create();
this->public.credmgr = credential_manager_create();
this->public.encoding = cred_encoding_create();
this->public.ocsp = ocsp_responders_create();
this->public.metadata = metadata_factory_create();
this->public.fetcher = fetcher_manager_create();
this->public.resolver = resolver_manager_create();

View File

@ -113,6 +113,7 @@
#include "credentials/credential_factory.h"
#include "credentials/credential_manager.h"
#include "credentials/cred_encoding.h"
#include "credentials/ocsp_responders.h"
#include "metadata/metadata_factory.h"
#include "utils/chunk.h"
#include "utils/capabilities.h"
@ -191,6 +192,11 @@ struct library_t {
*/
cred_encoding_t *encoding;
/**
* Manager for OCSP responders
*/
ocsp_responders_t *ocsp;
/**
* Registry and factory for metadata creation
*/

View File

@ -47,18 +47,17 @@ static bool plugin_cb(private_openxpki_plugin_t *this,
{
if (reg)
{
/* Is there already a registered OCSP responder? */
if (!lib->get(lib, "ocsp-responder"))
this->ocsp_responder = openxpki_ocsp_responder_create();
if (this->ocsp_responder)
{
this->ocsp_responder = openxpki_ocsp_responder_create();
lib->set(lib, "ocsp-responder", this->ocsp_responder);
lib->ocsp->add_responder(lib->ocsp, this->ocsp_responder);
}
}
else
{
if (this->ocsp_responder)
{
lib->set(lib, "ocsp-responder", NULL);
lib->ocsp->remove_responder(lib->ocsp, this->ocsp_responder);
this->ocsp_responder->destroy(this->ocsp_responder);
}
}

View File

@ -1088,6 +1088,14 @@ uint32_t chunk_hash(chunk_t chunk)
return chunk_mac(chunk, hash_key);
}
/*
* Described in header.
*/
uint32_t chunk_hash_ptr(chunk_t *chunk)
{
return chunk_hash(*chunk);
}
/**
* Described in header.
*/

View File

@ -414,6 +414,15 @@ void chunk_hash_seed();
*/
uint32_t chunk_hash(chunk_t chunk);
/**
* Same as chunk_hash() but takes a pointer to a chunk. Can be used in
* hashtables.
*
* @param chunk pointer to chunk to hash
* @return hash value
*/
uint32_t chunk_hash_ptr(chunk_t *chunk);
/**
* Incremental version of chunk_hash. Use this to hash two or more chunks.
*

View File

@ -22,6 +22,7 @@ pki_SOURCES = pki.c pki.h pki_cert.c pki_cert.h command.c command.h \
commands/signcrl.c \
commands/verify.c \
est/est_tls.h est/est_tls.c \
ocsp/index_responder.h ocsp/index_responder.c \
scep/scep.h scep/scep.c
pki_LDADD = \

View File

@ -116,20 +116,41 @@ int command_getopt(char **arg)
{
op = getopt_long(argc, argv, command_optstring, command_opts, NULL);
switch (op)
{
case '+':
case 'v':
continue;
default:
*arg = optarg;
return op;
}
}
}
/**
* Process options common for all commands
*/
static bool process_common_opts()
{
while (TRUE)
{
switch (getopt_long(argc, argv, command_optstring, command_opts, NULL))
{
case '+':
if (!options->from(options, optarg, &argc, &argv, optind))
{
/* a error value */
return 255;
return FALSE;
}
continue;
case 'v':
dbg_default_set_level(atoi(optarg));
continue;
default:
*arg = optarg;
return op;
continue;
case '?':
return FALSE;
case EOF:
return TRUE;
}
}
}
@ -279,6 +300,11 @@ int command_dispatch(int c, char *v[])
{
active = i;
build_opts();
if (!process_common_opts())
{
return command_usage("invalid options");
}
optind = 2;
return cmds[i].call();
}
}

View File

@ -18,12 +18,13 @@
#include <time.h>
#include "pki.h"
#include "ocsp/index_responder.h"
#include <collections/array.h>
#include <credentials/sets/mem_cred.h>
#include <credentials/certificates/ocsp_request.h>
#include <credentials/certificates/ocsp_response.h>
#include <credentials/certificates/ocsp_single_response.h>
#include <credentials/certificates/ocsp_responder.h>
/*
* Verifies the optional OCSP request signature generated by an OCSP requestor
@ -156,7 +157,7 @@ static bool find_issuer_cacert(hash_algorithm_t hashAlgorithm,
/*
* Find an OCSP signer certificate. Either the certificate of the CA itself that
* issued the end entitity certificate, the certificate of an OCSP signer
* issued the end-entity certificate, the certificate of an OCSP signer
* delegated by the CA via the standard OCSPSigning Extended Key Usage (EKU)
* flag or a self-signed OCSP signer certificate when multiple issuer OCSP
* requests have to be supported.
@ -181,7 +182,7 @@ static void find_ocsp_signer(certificate_t *first_issuer, bool *self_signed,
KEY_ANY, NULL, TRUE);
while (certs->enumerate(certs, &candidate))
{
/* get the flags and key identfiers of the candidate certificate */
/* get the flags and key identifiers of the candidate certificate */
x509 = (x509_t*)candidate;
flags = x509->get_flags(x509);
subKeyId = x509->get_subjectKeyIdentifier(x509);
@ -229,8 +230,9 @@ static int ocsp()
certificate_t *cacert = NULL, *first_issuer = NULL;
ocsp_request_t *ocsp_request;
ocsp_status_t ocsp_status = OCSP_SUCCESSFUL;
ocsp_responder_t *ocsp_responder = NULL;
ocsp_responder_t *index_responder = NULL;
linked_list_t *responses = NULL;
array_t *index_responders = NULL;
chunk_t encoding = chunk_empty, nonce = chunk_empty;
chunk_t issuerNameHash, issuerKeyHash, serialNumber;
hash_algorithm_t hashAlgorithm = HASH_SHA1, digest = HASH_UNKNOWN;
@ -286,6 +288,7 @@ static int ocsp()
creds->add_cert(creds, TRUE, cert);
continue;
case 'C':
DESTROY_IF(cacert);
cacert = lib->creds->create(lib->creds,
CRED_CERTIFICATE, CERT_X509,
BUILD_FROM_FILE, arg, BUILD_END);
@ -294,7 +297,7 @@ static int ocsp()
error = "parsing CA certificate failed";
goto usage;
}
creds->add_cert(creds, TRUE, cacert);
cacert = creds->add_cert_ref(creds, TRUE, cacert);
continue;
case 'l':
lifetime = atoi(arg) * 60;
@ -318,6 +321,21 @@ static int ocsp()
goto usage;
}
continue;
case 'x':
if (!cacert)
{
error = "--index must follow --cacert of corresponding CA";
goto usage;
}
index_responder = index_responder_create(cacert, arg);
if (!index_responder)
{
error = "invalid ---index value";
goto usage;
}
array_insert_create(&index_responders, ARRAY_TAIL,
index_responder);
continue;
case EOF:
break;
default:
@ -333,7 +351,7 @@ static int ocsp()
if (op == OP_RESPOND && !cacert)
{
error = "respond mode requires a ca certificate";
error = "respond mode requires a CA certificate";
goto end;
}
@ -406,18 +424,6 @@ static int ocsp()
DBG1(DBG_APP, "nonce: %#B", &nonce);
}
/* check for an ocsp responder */
if (op == OP_RESPOND)
{
ocsp_responder = lib->get(lib, "ocsp-responder");
if (!ocsp_responder)
{
DBG1(DBG_APP, " no ocsp-responder found");
ocsp_status = OCSP_INTERNALERROR;
goto gen;
}
}
/* enumerate over the ocsp requests and try to identify the issuers */
enumerator = ocsp_request->create_request_enumerator(ocsp_request);
while (enumerator->enumerate(enumerator, &hashAlgorithm, &issuerNameHash,
@ -474,9 +480,9 @@ static int ocsp()
if (issuer_cacert && (issuer_cacert == first_issuer || self_signed))
{
status = ocsp_responder->get_status(ocsp_responder,
issuer_cacert, serialNumber,
&revocationTime, &revocationReason);
status = lib->ocsp->get_status(lib->ocsp,
issuer_cacert, serialNumber,
&revocationTime, &revocationReason);
}
DBG1(DBG_APP, " certValidation: %N", cert_validation_names, status);
response->status = status;
@ -556,6 +562,8 @@ gen:
res = 0;
end:
array_destroy_offset(index_responders, offsetof(ocsp_responder_t, destroy));
DESTROY_IF(cacert);
DESTROY_IF(key);
lib->credmgr->remove_local_set(lib->credmgr, &creds->set);
creds->destroy(creds);
@ -572,6 +580,8 @@ end:
return res;
usage:
array_destroy_offset(index_responders, offsetof(ocsp_responder_t, destroy));
DESTROY_IF(cacert);
creds->destroy(creds);
return command_usage(error);
}
@ -583,7 +593,8 @@ static void __attribute__ ((constructor))reg()
{
command_register((command_t) {
ocsp, 'o', "ocsp", "OCSP responder",
{"[--in file] [--respond] [--cert file]+ [--key file]+ [--cacert file]+ ",
{"[--in file] [--respond] [--cert file]+ [--key file]+ ",
"[--cacert file [--index file]]+",
"[--digest md5|sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512]",
"[--rsa-padding pkcs1|pss] [--lifetime minutes]"},
{
@ -593,6 +604,7 @@ static void __attribute__ ((constructor))reg()
{"key", 'k', 1, "path to OCSP signing private key (can be used multiple times)"},
{"cert", 'c', 1, "path to OCSP signing certificate (can be used multiple times"},
{"cacert", 'C', 1, "CA certificate (can be used multiple times"},
{"index", 'x', 1, "OpenSSL-style index.txt to check status of certificates"},
{"digest", 'g', 1, "digest for signature creation, default: key-specific"},
{"rsa-padding", 'R', 1, "padding for RSA signatures, default: pkcs1"},
{"lifetime", 'l', 1, "validity in minutes of the OCSP response (if missing, nextUpdate is omitted)"},

View File

@ -18,6 +18,7 @@ pki \-\-ocsp \- OCSP request parser and OCSP responder.
.BI \-\-cacert\~ file
.BI \-\-key\~ file
.OP \-\-cert file
.OP \-\-index file
.OP \-\-lifetime minutes
.OP \-\-digest digest
.OP \-\-rsa\-padding padding
@ -87,6 +88,14 @@ OCSP signer key. Can be used multiple times.
OCSP signer certificate (if it is not a CA certificate). Can be used
multiple times.
.TP
.BI "\-x, \-\-index " file
OpenSSL-style index.txt providing information about the status of certificates
issued by the CA certificate loaded immediately before. Can be used multiple
times if the status for multiple CAs should be provided, just make sure to
pass each index.txt file right after the corresponding CA certificate.
See below for a description of the structure of these files.
.TP
.BI "\-l, \-\-lifetime " minutes
Validity in minutes of the OCSP response (if missing, nextUpdate is omitted).
.TP
@ -94,12 +103,52 @@ Validity in minutes of the OCSP response (if missing, nextUpdate is omitted).
Digest to use for signature creation. One of \fImd5\fR, \fIsha1\fR,
\fIsha224\fR, \fIsha256\fR, \fIsha384\fR, or \fIsha512\fR, \fIsha3_224\fR,
\fIsha3_256\fR, \fIsha3_384\fR, \fIsha3_512\fR. The default is
determined based on the type and size of the ocsp signing key.
determined based on the type and size of the OCSP signing key.
.TP
.BI "\-R, \-\-rsa\-padding " padding
Padding to use for RSA signatures. Either \fIpkcs1\fR or \fIpss\fR, defaults
to \fIpkcs1\fR.
.
.SH "INDEX.TXT DESCRIPTION"
.
Each line in an index.txt file consists of six columns that are separated by
tab characters:
The first column denotes the certificate status, which can be either "V" (for
valid), "E" (for expired, treated like valid), or "R" (for revoked).
The second column contains the certificate's expiration date and time in UTC in
the format YYMMDDHHMMSSZ. This field is ignored by the command but must not be
empty.
The third column is the revocation date and time in UTC in the format
YYMMDDHHMMSSZ and an optional revocation reason that immediately follows it,
separated by a comma. Valid reasons are "keyCompromise", "CACompromise",
"affiliationChanged", "superseded", "cessationOfOperation", "certificateHold",
and "removeFromCRL", any other value or omitting a reason results in
"unspecified".
The fourth column contains the certificate's serial number in
hexadecimal encoding.
The fifth and sixth columns are both ignored by the command, so they may be
omitted completely. They can contain a path to the certificate (usually set to
"unknown") and the certificate's subject DN with slashes separating the RDNs.
Example index.txt:
.PP
.EX
V 310930122422Z 03 unknown /C=CH/O=strongSwan/CN=moon...
V 310930122422Z 04 unknown /C=CH/O=strongSwan/CN=sun...
R 310930122422Z 231002122422Z,keyCompromise 88
V Z 05
.EE
.PP
Note that the fields are separated by tabs. So if a certificate is valid, two
tabs follow after the expiration date. The third line in this example only
specifies the relevant first four columns, the fourth even uses a dummy
expiration date.
.
.SH "EXAMPLES"
.
Show the raw content of an OCSP request:

View File

@ -0,0 +1,261 @@
/*
* Copyright (C) 2023 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
* This program 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 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program 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.
*/
#include <errno.h>
#include "index_responder.h"
#include <asn1/asn1.h>
#include <collections/hashtable.h>
typedef struct private_ocsp_responder_t private_ocsp_responder_t;
/**
* Private data
*/
struct private_ocsp_responder_t {
/**
* Public interface
*/
ocsp_responder_t public;
/**
* CA certificate
*/
certificate_t *ca;
/**
* Certificate database
*/
hashtable_t *certs;
};
/**
* Status information on a certificate
*/
typedef struct {
/** Serial number */
chunk_t serial;
/** Certificate's validity */
cert_validation_t validation;
/** Revocation reason */
crl_reason_t reason;
/** Revocation time */
time_t revocation_time;
} cert_entry_t;
/**
* Destroy a certificate status entry
*/
static void destroy_cert_entry(cert_entry_t *this)
{
chunk_free(&this->serial);
free(this);
}
METHOD(ocsp_responder_t, get_status, cert_validation_t,
private_ocsp_responder_t *this, certificate_t *cacert,
chunk_t serial_number, time_t *revocation_time, crl_reason_t *reason)
{
cert_entry_t *cert;
if (!cacert->equals(cacert, this->ca))
{
return VALIDATION_SKIPPED;
}
cert = this->certs->get(this->certs, &serial_number);
if (!cert)
{
return VALIDATION_FAILED;
}
if (revocation_time)
{
*revocation_time = cert->revocation_time;
}
if (reason)
{
*reason = cert->reason;
}
return cert->validation;
}
METHOD(ocsp_responder_t, destroy, void,
private_ocsp_responder_t *this)
{
lib->ocsp->remove_responder(lib->ocsp, &this->public);
this->certs->destroy_function(this->certs, (void*)destroy_cert_entry);
this->ca->destroy(this->ca);
free(this);
}
/*
* Described in header
*/
ocsp_responder_t *index_responder_create(certificate_t *ca, char *path)
{
private_ocsp_responder_t *this;
hashtable_t *certs;
char line[BUF_LEN], *token, *pos, *reason_str;
FILE *file;
file = fopen(path, "r");
if (!file)
{
fprintf(stderr, "failed to open '%s': %s\n", path, strerror(errno));
return NULL;
}
certs = hashtable_create((hashtable_hash_t)chunk_hash_ptr,
(hashtable_equals_t)chunk_equals_ptr, 32);
while (fgets(line, sizeof(line), file))
{
enumerator_t *enumerator;
cert_entry_t *cert;
cert_validation_t validation;
crl_reason_t reason = CRL_REASON_UNSPECIFIED;
time_t revocation_time = 0;
chunk_t revocation, serial;
bool valid = FALSE;
int i = 0;
switch (line[0])
{
case 'E':
/* OpenSSL only converts valid entries to expired ones */
case 'V':
validation = VALIDATION_GOOD;
break;
case 'R':
validation = VALIDATION_REVOKED;
break;
default:
/* ignore comments, empty lines etc. */
continue;
}
enumerator = enumerator_create_token(&line[1], "\t", " \n\r");
while (enumerator->enumerate(enumerator, &token))
{
switch (i++)
{
case 0: /* expiration date/time in UTC (YYMMDDHHMMSSZ), ignored */
continue;
case 1: /* if revoked, revocation date/time and optional reason */
if (validation == VALIDATION_REVOKED)
{
reason_str = NULL;
pos = strchr(token, ',');
if (pos)
{
*pos = '\0';
reason_str = ++pos;
/* OpenSSL may optionally store an OID if the reason
* is certificateHold (hold instruction code in
* RFC 3280, was removed with RFC 5280) */
pos = strchr(pos, ',');
if (pos)
{
*pos = '\0';
}
}
revocation = chunk_from_str(token);
revocation_time = asn1_to_time(&revocation,
ASN1_UTCTIME);
if (!revocation_time)
{
break;
}
if (strcaseeq(reason_str, "keyCompromise"))
{
reason = CRL_REASON_KEY_COMPROMISE;
}
else if (strcaseeq(reason_str, "CACompromise"))
{
reason = CRL_REASON_CA_COMPROMISE;
}
else if (strcaseeq(reason_str, "affiliationChanged"))
{
reason = CRL_REASON_AFFILIATION_CHANGED;
}
else if (strcaseeq(reason_str, "superseded"))
{
reason = CRL_REASON_SUPERSEDED;
}
else if (strcaseeq(reason_str, "cessationOfOperation"))
{
reason = CRL_REASON_CESSATION_OF_OPERATION;
}
else if (strcaseeq(reason_str, "certificateHold"))
{
reason = CRL_REASON_CERTIFICATE_HOLD;
validation = VALIDATION_ON_HOLD;
}
else if (strcaseeq(reason_str, "removeFromCRL"))
{
reason = CRL_REASON_REMOVE_FROM_CRL;
}
else
{
reason = CRL_REASON_UNSPECIFIED;
}
continue;
}
i++;
/* if not revoked, this field is empty, fall-through */
case 2: /* hexadecimal serial number */
serial = chunk_from_hex(chunk_from_str(token), NULL);
valid = serial.len > 0;
/* skip the last two fields, an optional path, usually set
* to "unknown", and the subject DN (RDNs separated by
* slashes), which we don't use */
break;
default:
break;
}
break;
}
enumerator->destroy(enumerator);
if (valid)
{
INIT(cert,
.serial = serial,
.validation = validation,
.reason = reason,
.revocation_time = revocation_time,
);
cert = certs->put(certs, &cert->serial, cert);
if (cert)
{
destroy_cert_entry(cert);
}
}
}
fclose(file);
INIT(this,
.public = {
.get_status = _get_status,
.destroy = _destroy,
},
.ca = ca->get_ref(ca),
.certs = certs,
);
lib->ocsp->add_responder(lib->ocsp, &this->public);
DBG1(DBG_APP, "loaded status of %u certificates issued by '%Y' from %s",
certs->get_count(certs), ca->get_subject(ca), path);
return &this->public;
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
* This program 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 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program 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.
*/
/**
* @defgroup ocsp ocsp
* @{ @ingroup pki
*/
#ifndef INDEX_RESPONDER_H_
#define INDEX_RESPONDER_H_
#include <credentials/certificates/ocsp_responder.h>
/**
* Create an index.txt-based OCSP responder for the given CA and file.
*
* On success, the responder is automatically registered until destroyed.
*
* @param ca CA certificate (referenced)
* @param path path to index.txt
* @return OCSP responder, NULL if file is invalid
*/
ocsp_responder_t *index_responder_create(certificate_t *ca, char *path);
#endif /** INDEX_RESPONDER_H_ @} */

View File

@ -5,7 +5,5 @@ cd /etc/ca
echo "Content-type: application/ocsp-response"
echo ""
cat | /usr/bin/openssl ocsp -index index.txt -CA strongswanCert.pem \
-rkey ocspKey.pem -rsigner ocspCert.pem \
-nmin 5 \
-reqin /dev/stdin -respout /dev/stdout | cat
cat | pki --ocsp --respond --cacert strongswanCert.pem --index index.txt \
--cert ocspCert.pem --key ocspKey.pem --lifetime 5 --debug 0

View File

@ -5,7 +5,5 @@ cd /etc/ca/research
echo "Content-type: application/ocsp-response"
echo ""
cat | /usr/bin/openssl ocsp -index index.txt -CA researchCert.pem \
-rkey ocspKey.pem -rsigner ocspCert.pem \
-nmin 5 \
-reqin /dev/stdin -respout /dev/stdout | cat
cat | pki --ocsp --respond --cacert researchCert.pem --index index.txt \
--cert ocspCert.pem --key ocspKey.pem --lifetime 5 --debug 0

View File

@ -5,7 +5,5 @@ cd /etc/ca/sales
echo "Content-type: application/ocsp-response"
echo ""
cat | /usr/bin/openssl ocsp -index index.txt -CA salesCert.pem \
-rkey ocspKey.pem -rsigner ocspCert.pem \
-nmin 5 \
-reqin /dev/stdin -respout /dev/stdout | cat
cat | pki --ocsp --respond --cacert salesCert.pem --index index.txt \
--cert ocspCert.pem --key ocspKey.pem --lifetime 5 --debug 0

View File

@ -11,11 +11,11 @@ connections {
remote {
auth = pubkey
cacerts = researchCert.pem
revocation = ifuri
revocation = ifuri
}
children {
alice {
local_ts = 10.1.0.10/32
local_ts = 10.1.0.10/32
esp_proposals = aes128-sha256-ecp256
}
}
@ -34,7 +34,7 @@ connections {
remote {
auth = pubkey
cacerts = salesCert.pem
revocation = ifuri
revocation = ifuri
}
children {
venus {
@ -56,11 +56,11 @@ authorities {
research {
cacert = researchCert.pem
ocsp_uris = http://ocsp.strongswan.org:8881
ocsp_uris = http://ocsp.strongswan.org:8880
}
sales {
cacert = salesCert.pem
ocsp_uris = http://ocsp.strongswan.org:8882
ocsp_uris = http://ocsp.strongswan.org:8880
}
}

View File

@ -0,0 +1,15 @@
#!/bin/bash
cd /etc/ca
echo "Content-type: application/ocsp-response"
echo ""
cat | pki --ocsp --respond \
--cacert strongswanCert.pem --index index.txt \
--cert ocspCert.pem --key ocspKey.pem \
--cacert research/researchCert.pem --index research/index.txt \
--cert research/ocspCert.pem --key research/ocspKey.pem \
--cacert sales/salesCert.pem --index sales/index.txt \
--cert sales/ocspCert.pem --key sales/ocspKey.pem \
--lifetime 5 --debug 0

View File

@ -5,7 +5,5 @@ cd /etc/ca
echo "Content-type: application/ocsp-response"
echo ""
cat | /usr/bin/openssl ocsp -index index.txt -CA strongswanCert.pem \
-rkey ocspKey-self.pem -rsigner ocspCert-self.pem \
-resp_no_certs -nmin 5 \
-reqin /dev/stdin -respout /dev/stdout | cat
cat | pki --ocsp --respond --cacert strongswanCert.pem --index index.txt \
--cert ocspCert-self.pem --key ocspKey-self.pem --lifetime 5 --debug 0

View File

@ -5,7 +5,8 @@ cd /etc/ca
echo "Content-type: application/ocsp-response"
echo ""
# we have to use OpenSSL here as pki --ocsp rejects signing with such a
# non-OCSP-signer certificate
cat | /usr/bin/openssl ocsp -index index.txt -CA strongswanCert.pem \
-rkey winnetouKey.pem -rsigner winnetouCert.pem \
-nmin 5 \
-reqin /dev/stdin -respout /dev/stdout | cat
-nmin 5 -reqin /dev/stdin -respout /dev/stdout | cat

View File

@ -5,7 +5,5 @@ cd /etc/ca
echo "Content-type: application/ocsp-response"
echo ""
cat | /usr/bin/openssl ocsp -index index.txt -CA strongswanCert.pem \
-rkey strongswanKey.pem -rsigner strongswanCert.pem \
-resp_no_certs -nmin 5 \
-reqin /dev/stdin -respout /dev/stdout | cat
cat | pki --ocsp --respond --cacert strongswanCert.pem --index index.txt \
--key strongswanKey.pem --lifetime 5 --debug 0

View File

@ -8,7 +8,5 @@ echo ""
# simulate a delayed response
sleep 2
cat | /usr/bin/openssl ocsp -index index.txt -CA strongswanCert.pem \
-rkey ocspKey.pem -rsigner ocspCert.pem \
-nmin 5 \
-reqin /dev/stdin -respout /dev/stdout | cat
cat | pki --ocsp --respond --cacert strongswanCert.pem --index index.txt \
--cert ocspCert.pem --key ocspKey.pem --lifetime 5 --debug 0

View File

@ -8,7 +8,5 @@ echo ""
# simulate a delayed response
sleep 2
cat | /usr/bin/openssl ocsp -index index.txt -CA strongswanCert.pem \
-rkey ocspKey.pem -rsigner ocspCert.pem \
-nmin 5 \
-reqin /dev/stdin -respout /dev/stdout | cat
cat | pki --ocsp --respond --cacert strongswanCert.pem --index index.txt \
--cert ocspCert.pem --key ocspKey.pem --lifetime 5 --debug 0

View File

@ -5,7 +5,5 @@ cd /etc/ca
echo "Content-type: application/ocsp-response"
echo ""
cat | /usr/bin/openssl ocsp -index index.txt -CA strongswanCert.pem \
-rkey ocspKey-self.pem -rsigner ocspCert-self.pem \
-nmin 5 \
-reqin /dev/stdin -respout /dev/stdout | cat
cat | pki --ocsp --respond --cacert strongswanCert.pem --index index.txt \
--cert ocspCert-self.pem --key ocspKey-self.pem --lifetime 5 --debug 0