Or, yet another attempt at starting a discussion on this... :/

(I kinda lost track of the feature freeze date, if any has been decided for
this release cycle, so apologies if this comes at the wrong moment).

I attached the patches that implement OCSP stapling for both GnuTLS and NSS
backends, and the --cert-status option for curl. They also include documentation
for both the libcurl and curl options.

So, the GnuTLS and NSS backends are, AFAICT, fully functional. The failures I
was seeing in the GnuTLS backend were caused by a bug in GnuTLS itself, which
got fixed in the 3.3.11 release. You may still see failures due to a bug in
libtasn1 (used by GnuTLS), which got fixed in the 4.2 release (for reference
see [0] and [1]).

As for the OpenSSL (which I left out for now) backend, I'm pretty sure OpenSSL's
OCSP support is broken, since it requires the issuer certificate to be in the
trust store (which basically means that e.g. an intermediate certificate needs
to be in the store, even if it's itself signed by a CA certificate). Notably,
this breaks pretty much all CloudFlare sites (or any sites that use intermediate
certificates) unless those issuers are trusted with --capath/--cacert. I haven't
looked into this yet, but I'll probably file a bug report at some point, and
finish up the curl support if/when this gets fixed.

Even without OpenSSL support (which can be added later on), I think this is
ready to be merged. For testing, you can use the following websites that support
OCSP stapling:

https://yahoo.com
https://mozilla.org
https://tn123.org
https://digitalocean.com (from CloudFlare)
https://kuix.de:5148
https://kuix.de:5149 (this got its certificate revoked, so the check must fail)

I've kept the patches separate to ease development and review, but I can merge
them if needed.

Cheers

[0] https://bugs.debian.org/772055
[1] https://bugs.debian.org/759161
From 716621191cedb61c8972e68320a3d9aa99a7682c Mon Sep 17 00:00:00 2001
From: Alessandro Ghedini <[email protected]>
Date: Mon, 16 Jun 2014 13:20:47 +0200
Subject: [PATCH 1/4] url: add CURLOPT_SSL_VERIFYSTATUS option

This option can be used to enable/disable certificate status verification. The
certificate status can be obtained by using the "Certificate Status Request" TLS
extension defined in RFC6066 section 8, or by using the OCSP protocol defined in
RFC6960.

This also adds the CURLE_SSL_INVALIDCERTSTATUS error, to be used when the
certificate status verification fails.
---
 docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.3 | 52 ++++++++++++++++++++++++++++
 include/curl/curl.h                          |  4 +++
 lib/url.c                                    |  6 ++++
 lib/urldata.h                                |  1 +
 4 files changed, 63 insertions(+)
 create mode 100644 docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.3

diff --git a/docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.3 b/docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.3
new file mode 100644
index 0000000..ffbbe96
--- /dev/null
+++ b/docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.3
@@ -0,0 +1,52 @@
+.\" **************************************************************************
+.\" *                                  _   _ ____  _
+.\" *  Project                     ___| | | |  _ \| |
+.\" *                             / __| | | | |_) | |
+.\" *                            | (__| |_| |  _ <| |___
+.\" *                             \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 1998 - 2014, Daniel Stenberg, <[email protected]>, et al.
+.\" *
+.\" * This software is licensed as described in the file COPYING, which
+.\" * you should have received as part of this distribution. The terms
+.\" * are also available at http://curl.haxx.se/docs/copyright.html.
+.\" *
+.\" * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+.\" * copies of the Software, and permit persons to whom the Software is
+.\" * furnished to do so, under the terms of the COPYING file.
+.\" *
+.\" * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+.\" * KIND, either express or implied.
+.\" *
+.\" **************************************************************************
+.\"
+.TH CURLOPT_SSL_VERIFYSTATUS 3 "04 Dec 2014" "libcurl 7.40.0" "curl_easy_setopt options"
+.SH NAME
+CURLOPT_SSL_VERIFYSTATUS \- verify the certificate's status
+.SH SYNOPSIS
+#include <curl/curl.h>
+
+CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSL_VERIFYSTATUS, long verify);
+.SH DESCRIPTION
+Pass a long as parameter to enable or disable.
+
+This option determines whether libcurl verifies the status of the server cert
+using the "Certificate Status Request" TLS extension (aka. OCSP stapling).
+
+Note that if this option is enabled but the server does not support the TLS
+extension, the verification will fail.
+
+.SH DEFAULT
+0
+.SH PROTOCOLS
+All TLS based protocols: HTTPS, FTPS, IMAPS, POP3, SMTPS etc.
+.SH EXAMPLE
+TODO
+.SH AVAILABILITY
+This is currently only supported by the GnuTLS and NSS TLS backends.
+.SH RETURN VALUE
+Returns CURLE_OK if TLS is enabled, CURLE_UNKNOWN_OPTION if not.
+.SH "SEE ALSO"
+.BR CURLOPT_SSL_VERIFYHOST "(3), "
+.BR CURLOPT_SSL_VERIFYPEER "(3), "
+.BR CURLOPT_CAINFO "(3), "
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 0915a20..d334087 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -523,6 +523,7 @@ typedef enum {
                                     session will be queued */
   CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not
                                      match */
+  CURLE_SSL_INVALIDCERTSTATUS,   /* 91 - invalid certificate status */
   CURL_LAST /* never use! */
 } CURLcode;
 
@@ -1622,6 +1623,9 @@ typedef enum {
   /* Path to UNIX domain socket */
   CINIT(UNIX_SOCKET_PATH, OBJECTPOINT, 231),
 
+  /* Set if we should verify the certificate status. */
+  CINIT(SSL_VERIFYSTATUS, LONG, 232),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
diff --git a/lib/url.c b/lib/url.c
index d191678..6762a6f 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -1996,6 +1996,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
 
     data->set.ssl.verifyhost = (0 != arg)?TRUE:FALSE;
     break;
+  case CURLOPT_SSL_VERIFYSTATUS:
+    /*
+     * Enable certificate status verifying.
+     */
+    data->set.ssl.verifystatus = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
   case CURLOPT_SSL_CTX_FUNCTION:
 #ifdef have_curlssl_ssl_ctx
     /*
diff --git a/lib/urldata.h b/lib/urldata.h
index a2dc1c3..4efa466 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -366,6 +366,7 @@ struct ssl_config_data {
 
   bool verifypeer;       /* set TRUE if this is desired */
   bool verifyhost;       /* set TRUE if CN/SAN must match hostname */
+  bool verifystatus;     /* set TRUE if certificate status must be checked */
   char *CApath;          /* certificate dir (doesn't work on windows) */
   char *CAfile;          /* certificate to verify peer against */
   const char *CRLfile;   /* CRL to check certificate revocation */
-- 
2.1.3

From 43039da0bb15885a5fc04430e5f065b4dbbeb5d9 Mon Sep 17 00:00:00 2001
From: Alessandro Ghedini <[email protected]>
Date: Mon, 16 Jun 2014 13:21:02 +0200
Subject: [PATCH 2/4] gtls: add support for the Certificate Status Request TLS
 extension

Also known as "status_request" or OCSP stapling, defined in RFC6066 section 8.

This requires GnuTLS 3.1.3 or higher to build, however it's recommended to use
at least GnuTLS 3.3.11 since previous versions had a bug that caused the OCSP
response verfication to fail even on valid responses.
---
 lib/vtls/gtls.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
index a9c42c2..837f772 100644
--- a/lib/vtls/gtls.c
+++ b/lib/vtls/gtls.c
@@ -98,6 +98,14 @@ static bool gtls_inited = FALSE;
 #      define HAS_ALPN
 #    endif
 #  endif
+
+#  if (GNUTLS_VERSION_NUMBER >= 0x03020d)
+#    define HAS_OCSP
+#  endif
+#endif
+
+#ifdef HAS_OCSP
+# include <gnutls/ocsp.h>
 #endif
 
 /*
@@ -663,6 +671,16 @@ gtls_connect_step1(struct connectdata *conn,
   /* lowat must be set to zero when using custom push and pull functions. */
   gnutls_transport_set_lowat(session, 0);
 
+#ifdef HAS_OCSP
+  if(data->set.ssl.verifystatus) {
+    rc = gnutls_ocsp_status_request_enable_client(session, NULL, 0, NULL);
+    if(rc != GNUTLS_E_SUCCESS) {
+      failf(data, "gnutls_ocsp_status_request_enable_client() failed: %d", rc);
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+#endif
+
   /* This might be a reconnect, so we check for a session ID in the cache
      to speed up things */
 
@@ -822,6 +840,23 @@ gtls_connect_step3(struct connectdata *conn,
   else
     infof(data, "\t server certificate verification SKIPPED\n");
 
+#ifdef HAS_OCSP
+  if(data->set.ssl.verifystatus) {
+    if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) {
+      if(verify_status & GNUTLS_CERT_REVOKED)
+        failf(data, "SSL server certificate was REVOKED\n");
+      else
+        failf(data, "SSL server certificate status verification FAILED");
+
+      return CURLE_SSL_INVALIDCERTSTATUS;
+    }
+    else
+      infof(data, "SSL server certificate status verification OK\n");
+  }
+  else
+    infof(data, "SSL server certificate status verification SKIPPED\n");
+#endif
+
   /* initialize an X.509 certificate structure. */
   gnutls_x509_crt_init(&x509_cert);
 
-- 
2.1.3

From 9fc4ca255ec1198b0e5428b543b1242a7d9a197c Mon Sep 17 00:00:00 2001
From: Alessandro Ghedini <[email protected]>
Date: Tue, 24 Jun 2014 23:25:59 +0200
Subject: [PATCH 3/4] nss: add support for the Certificate Status Request TLS
 extension

Also known as "status_request" or OCSP stapling, defined in RFC6066 section 8.

This requires NSS 3.15 or higher.
---
 lib/vtls/nss.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c
index f26cba0..251c21f 100644
--- a/lib/vtls/nss.c
+++ b/lib/vtls/nss.c
@@ -638,6 +638,34 @@ static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig,
                                     PRBool isServer)
 {
   struct connectdata *conn = (struct connectdata *)arg;
+
+#ifdef SSL_ENABLE_OCSP_STAPLING
+  if(conn->data->set.ssl.verifystatus) {
+    SECStatus cacheResult;
+
+    const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd);
+    if(!csa) {
+      failf(conn->data, "Invalid OCSP response");
+      return SECFailure;
+    }
+
+    if(csa->len == 0) {
+      failf(conn->data, "No OCSP response received");
+      return SECFailure;
+    }
+
+    cacheResult = CERT_CacheOCSPResponseFromSideChannel(
+      CERT_GetDefaultCertDB(), SSL_PeerCertificate(fd),
+      PR_Now(), &csa->items[0], arg
+    );
+
+    if(cacheResult != SECSuccess) {
+      failf(conn->data, "Invalid OCSP response");
+      return cacheResult;
+    }
+  }
+#endif
+
   if(!conn->data->set.ssl.verifypeer) {
     infof(conn->data, "skipping SSL peer certificate verification\n");
     return SECSuccess;
@@ -1609,6 +1637,13 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
     SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]);
   }
 
+#ifdef SSL_ENABLE_OCSP_STAPLING
+  if(data->set.ssl.verifystatus) {
+    if(SSL_OptionSet(connssl->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE) != SECSuccess)
+      goto error;
+  }
+#endif
+
 #ifdef USE_NGHTTP2
   if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
 #ifdef SSL_ENABLE_NPN
-- 
2.1.3

From 2eafd6dddb0f8ca3571b602b3057d838826c365d Mon Sep 17 00:00:00 2001
From: Alessandro Ghedini <[email protected]>
Date: Mon, 16 Jun 2014 20:47:26 +0200
Subject: [PATCH 4/4] curl: add --cert-status option

This enable the CURLOPT_SSL_VERIFYSTATUS functionality.
---
 docs/curl.1         | 10 ++++++++++
 src/tool_cfgable.h  |  1 +
 src/tool_getparam.c |  5 +++++
 src/tool_operate.c  |  3 +++
 4 files changed, 19 insertions(+)

diff --git a/docs/curl.1 b/docs/curl.1
index 1f5389a..25a3739 100644
--- a/docs/curl.1
+++ b/docs/curl.1
@@ -552,6 +552,16 @@ This is currently only implemented in the OpenSSL, GnuTLS and GSKit backends.
 
 If this option is used several times, the last one will be used.
 (Added in 7.39.0)
+.IP "--cert-status"
+(SSL) Tells curl to verify the status of the server certificate by using the
+Certificate Status Request (aka. OCSP stapling) TLS extension.
+
+If this option is enabled and the server sends an invalid (e.g. expired)
+response, if the response suggests that the server certificate has been revoked,
+or no response at all is received, the verification fails.
+
+This is currently only implemented in the GnuTLS and NSS backends.
+(Added in 7.40.0)
 .IP "-f, --fail"
 (HTTP) Fail silently (no output at all) on server errors. This is mostly done
 to better enable scripts etc to better deal with failed attempts. In normal
diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h
index 2336016..2ab79f0 100644
--- a/src/tool_cfgable.h
+++ b/src/tool_cfgable.h
@@ -126,6 +126,7 @@ struct OperationConfig {
   bool globoff;
   bool use_httpget;
   bool insecure_ok;         /* set TRUE to allow insecure SSL connects */
+  bool verifystatus;
   bool create_dirs;
   bool ftp_create_dirs;
   bool ftp_skip_ip;
diff --git a/src/tool_getparam.c b/src/tool_getparam.c
index 3932ccb..ee198c3 100644
--- a/src/tool_getparam.c
+++ b/src/tool_getparam.c
@@ -217,6 +217,7 @@ static const struct LongShort aliases[]= {
   {"En", "ssl-allow-beast",          FALSE},
   {"Eo", "login-options",            TRUE},
   {"Ep", "pinnedpubkey",             TRUE},
+  {"Eq", "cert-status",              FALSE},
   {"f",  "fail",                     FALSE},
   {"F",  "form",                     TRUE},
   {"Fs", "form-string",              TRUE},
@@ -1363,6 +1364,10 @@ ParameterError getparameter(char *flag,    /* f or -long-flag */
         GetStr(&config->pinnedpubkey, nextarg);
         break;
 
+      case 'q': /* --cert-status */
+        config->verifystatus = TRUE;
+        break;
+
       default: /* certificate file */
       {
         char *certname, *passphrase;
diff --git a/src/tool_operate.c b/src/tool_operate.c
index 3fa0db1..8d0a7ec 100644
--- a/src/tool_operate.c
+++ b/src/tool_operate.c
@@ -1038,6 +1038,9 @@ static CURLcode operate_do(struct GlobalConfig *global,
             /* libcurl default is strict verifyhost -> 2L   */
             /* my_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); */
           }
+
+          if(config->verifystatus)
+            my_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
         }
 
         if(built_in_protos & (CURLPROTO_SCP|CURLPROTO_SFTP)) {
-- 
2.1.3

Attachment: signature.asc
Description: Digital signature

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html

Reply via email to