]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
pki: Add option to load certificate status information from index.txt
authorTobias Brunner <tobias@strongswan.org>
Mon, 30 Oct 2023 17:12:12 +0000 (18:12 +0100)
committerTobias Brunner <tobias@strongswan.org>
Mon, 13 Nov 2023 11:50:47 +0000 (12:50 +0100)
Each index.txt is associated with the most recently loaded CA
certificate.

src/pki/commands/ocsp.c
src/pki/man/pki---ocsp.1.in

index 0dd5b4a780b005518c3c391b596205893f05a5f5..81e732cf494105f6421ed790896d153e67af8c55 100644 (file)
@@ -18,7 +18,9 @@
 #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>
@@ -155,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.
@@ -180,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);
@@ -228,7 +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 *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;
@@ -284,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);
@@ -292,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;
@@ -316,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:
@@ -331,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;
        }
 
@@ -542,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);
@@ -558,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);
 }
@@ -569,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]"},
                {
@@ -579,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)"},
index 23d4c7de4f1ac94bbee53e5db943f43fd856079e..54ae002249f978659b9c56d9adb6c0b1fc4c8127 100644 (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: