]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
gnutls-cli: Add option to store all stapled OCSP responses
authorFiona Klute <fiona.klute@gmx.de>
Sat, 8 Feb 2020 22:47:17 +0000 (23:47 +0100)
committerFiona Klute <fiona.klute@gmx.de>
Sat, 8 Feb 2020 22:54:52 +0000 (23:54 +0100)
Note that there's a small modification to the behavior of the existing
--ocsp-save option: If there is no stapled OCSP response the output
file is still created and will be empty.

Signed-off-by: Fiona Klute <fiona.klute@gmx.de>
src/cli-args.def
src/cli.c

index 12b8bb05db8084439b71186d348ac2dddf632ab5..a8760fab9064327d4f3f4705efe2648161d725f7 100644 (file)
@@ -172,7 +172,16 @@ flag = {
     name      = save-ocsp;
     arg-type  = string;
     descrip   = "Save the peer's OCSP status response in the provided file";
-    doc      = "";
+    doc       = "";
+    flags-cant = save-ocsp-multi;
+};
+
+flag = {
+    name      = save-ocsp-multi;
+    arg-type  = string;
+    descrip   = "Save all OCSP responses provided by the peer in this file";
+    doc       = "The file will contain a list of PEM encoded OCSP status responses if any were provided by the peer, starting with the one for the peer's server certificate.";
+    flags-cant = save-ocsp;
 };
 
 flag = {
index f02f842b719f9294eb6e20b578fd130f62f5c50d..db072b9303afbea9e3abf08b44cf68f9b4448fbb 100644 (file)
--- a/src/cli.c
+++ b/src/cli.c
@@ -358,6 +358,84 @@ static void try_save_cert(gnutls_session_t session)
        return;
 }
 
+static void try_save_ocsp_status(gnutls_session_t session)
+{
+       unsigned int cert_num = 0;
+       gnutls_certificate_get_peers(session, &cert_num);
+       if (cert_num == 0) {
+               fprintf(stderr, "no certificates sent by server, so can't get OCSP status!\n");
+               return;
+       }
+
+       const char *path;
+       gnutls_x509_crt_fmt_t type;
+       unsigned int max_out;
+
+       /* This function is called if exactly one of SAVE_OCSP and
+        * SAVE_OCSP_MULTI is set. */
+       if (HAVE_OPT(SAVE_OCSP))
+       {
+               path = OPT_ARG(SAVE_OCSP);
+               type = GNUTLS_X509_FMT_DER;
+               max_out = 1;
+       } else {
+               path = OPT_ARG(SAVE_OCSP_MULTI);
+               type = GNUTLS_X509_FMT_PEM;
+               max_out = cert_num;
+       }
+
+       FILE *fp = fopen(path, "w");
+       if (fp == NULL) {
+               fprintf(stderr, "could not open %s for writing\n", path);
+               exit(1);
+       }
+
+       for (unsigned int i = 0; i < max_out; i++) {
+               gnutls_datum_t oresp;
+               int ret = gnutls_ocsp_status_request_get2(session, i, &oresp);
+               if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+                       fprintf(stderr, "no OCSP response for certificate %u\n", i);
+                       continue;
+               } else if (ret < 0) {
+                       fprintf(stderr, "error getting OCSP response %u: %s\n",
+                               i, gnutls_strerror(ret));
+                       exit(1);
+               }
+
+               if (type == GNUTLS_X509_FMT_DER) {
+                       /* on success the return value is equal to the
+                        * number of items (third parameter) */
+                       if (fwrite(oresp.data, oresp.size, 1, fp) != 1) {
+                               fprintf(stderr, "writing to %s failed\n", path);
+                               exit(1);
+                       }
+                       continue;
+               }
+
+               gnutls_datum_t t;
+               ret = gnutls_pem_base64_encode_alloc("OCSP RESPONSE",
+                                                    &oresp, &t);
+               if (ret < 0) {
+                       fprintf(stderr, "error allocating PEM OCSP response: %s\n",
+                               gnutls_strerror(ret));
+                       exit(1);
+               }
+
+               /* on success the return value is equal to the number
+                * of items (third parameter) */
+               if (fwrite(t.data, t.size, 1, fp) != 1) {
+                       fprintf(stderr, "writing to %s failed\n", path);
+                       exit(1);
+               }
+               gnutls_free(t.data);
+       }
+       if (fclose(fp) != 0) {
+               perror("failed to close OCSP save file");
+       }
+
+       return;
+}
+
 static int cert_verify_callback(gnutls_session_t session)
 {
        int rc;
@@ -367,7 +445,6 @@ static int cert_verify_callback(gnutls_session_t session)
        int dane = ENABLED_OPT(DANE);
        int ca_verify = ENABLED_OPT(CA_VERIFICATION);
        const char *txt_service;
-       gnutls_datum_t oresp;
        const char *host;
 
        /* On an session with TOFU the PKI/DANE verification
@@ -390,23 +467,12 @@ static int cert_verify_callback(gnutls_session_t session)
        }
 
 #ifndef ENABLE_OCSP
-       if (HAVE_OPT(SAVE_OCSP) || HAVE_OPT(OCSP)) {
+       if (HAVE_OPT(SAVE_OCSP_MULTI) || HAVE_OPT(SAVE_OCSP) || HAVE_OPT(OCSP)) {
                fprintf(stderr, "OCSP is not supported!\n");
        }
 #else
-       rc = gnutls_ocsp_status_request_get(session, &oresp);
-       if (rc < 0) {
-               oresp.data = NULL;
-               oresp.size = 0;
-       }
-
-       if (HAVE_OPT(SAVE_OCSP) && oresp.data) {
-               FILE *fp = fopen(OPT_ARG(SAVE_OCSP), "w");
-
-               if (fp != NULL) {
-                       fwrite(oresp.data, 1, oresp.size, fp);
-                       fclose(fp);
-               }
+       if (HAVE_OPT(SAVE_OCSP_MULTI) || HAVE_OPT(SAVE_OCSP)) {
+               try_save_ocsp_status(session);
        }
 #endif