Ok, so this work was done by Marko Kreen, all as the result of a very long 
discussion in:

https://github.com/libressl-portable/openbsd/pull/47

In a nutshell, I threw down a glove that libtls could have functions to support 
OCSP, and
make it where a client could write ocsp stuff, but I would resist making libtls 
be
and http library that does that for you.  I challenged him to add the necessary 
support
functions so it was possible to write a client. 

He delivered, and I've cleaned a few things up in it. (after a long delay which
I apologize for)

Attached to this message is marko's test program, which uses libcurl - The diff 
is
for our libtls, and I've been able to compile and use his test program with it:

$ ./oc amazon.com
libssl: LibreSSL 2.4.1
OCSP stapling: good
  req_status=0 cert_status=0 crl_reason=0
  this update: Mon Jul  4 08:17:21 2016
  next update: Mon Jul 11 08:17:21 2016
  revocation: --
OCSP URL: http://ss.symcd.com
OCSP responder: good
  req_status=0 cert_status=0 crl_reason=0
  this update: Mon Jul  4 08:17:21 2016
  next update: Mon Jul 11 08:17:21 2016
  revocation: --
$ ./oc google.com
libssl: LibreSSL 2.4.1
OCSP stapling: no-ocsp
OCSP URL: http://clients1.google.com/ocsp
OCSP responder: good
  req_status=0 cert_status=0 crl_reason=0
  this update: Tue Jul  5 13:00:28 2016
  next update: Tue Jul 12 13:00:28 2016
  revocation: --
$ 

Discussion, OK's

diff --git lib/libtls/Makefile lib/libtls/Makefile
index ca2f00b..461bf44 100644
--- lib/libtls/Makefile
+++ lib/libtls/Makefile
@@ -19,6 +19,7 @@ SRCS= tls.c \
        tls_peer.c \
        tls_server.c \
        tls_util.c \
+       tls_ocsp.c \
        tls_verify.c
 
 MAN=   tls_init.3
diff --git lib/libtls/tls.c lib/libtls/tls.c
index 76d00e5..b00bea8 100644
--- lib/libtls/tls.c
+++ lib/libtls/tls.c
@@ -393,6 +393,13 @@ tls_reset(struct tls *ctx)
        tls_free_conninfo(ctx->conninfo);
        free(ctx->conninfo);
        ctx->conninfo = NULL;
+
+       tls_ocsp_info_free(ctx->ocsp_info);
+       ctx->ocsp_info = NULL;
+       ctx->ocsp_result = NULL;
+
+       if (ctx->flags & TLS_OCSP_CLIENT)
+               tls_ocsp_client_free(ctx);
 }
 
 int
diff --git lib/libtls/tls.h lib/libtls/tls.h
index 75c46c1..da6cd69 100644
--- lib/libtls/tls.h
+++ lib/libtls/tls.h
@@ -40,6 +40,29 @@ extern "C" {
 
 #define TLS_WANT_POLLIN                -2
 #define TLS_WANT_POLLOUT       -3
+#define TLS_NO_OCSP            -4
+
+#define TLS_OCSP_RESPONSE_SUCCESSFUL           0
+#define TLS_OCSP_RESPONSE_MALFORMED            1
+#define TLS_OCSP_RESPONSE_INTERNALERR          2
+#define TLS_OCSP_RESPONSE_TRYLATER             3
+#define TLS_OCSP_RESPONSE_SIGREQUIRED          5
+#define TLS_OCSP_RESPONSE_UNAUTHORIZED         6
+
+#define TLS_OCSP_CERT_GOOD                     0
+#define TLS_OCSP_CERT_REVOKED                  1
+#define TLS_OCSP_CERT_UNKNOWN                  2
+
+#define TLS_CRL_REASON_UNPSECIFIED             0
+#define TLS_CRL_REASON_KEY_COMPROMISE          1
+#define TLS_CRL_REASON_CA_COMPROMISE           2
+#define TLS_CRL_REASON_AFFILIATION_CHANGED     3
+#define TLS_CRL_REASON_SUPERSEDED              4
+#define TLS_CRL_REASON_CESSATION_OF_OPERATION  5
+#define TLS_CRL_REASON_CERTIFICATE_HOLD                6
+#define TLS_CRL_REASON_REMOVE_FROM_CRL         8
+#define TLS_CRL_REASON_PRIVILEGE_WITH_DRAWN    9
+#define TLS_CRL_REASON_AA_COMPROMISE           10
 
 struct tls;
 struct tls_config;
@@ -70,6 +93,8 @@ int tls_config_set_keypair_file(struct tls_config *_config,
     const char *_cert_file, const char *_key_file);
 int tls_config_set_keypair_mem(struct tls_config *_config, const uint8_t 
*_cert,
     size_t _cert_len, const uint8_t *_key, size_t _key_len);
+int tls_config_set_ocsp_stapling_file(struct tls_config *_config, const char 
*_blob_file);
+int tls_config_set_ocsp_stapling_mem(struct tls_config *_config, const uint8_t 
*_blob, size_t _len);
 void tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols);
 void tls_config_set_verify_depth(struct tls_config *_config, int 
_verify_depth);
 
@@ -121,6 +146,18 @@ const char *tls_conn_cipher(struct tls *_ctx);
 
 uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password);
 
+int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, 
int *crl_reason,
+                     time_t *this_update, time_t *next_update, time_t 
*revoction_time,
+                     const char **result_text);
+
+int tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target,
+                           char **ocsp_url, void **request_blob, size_t 
*request_size);
+
+int tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p, struct 
tls_config *config,
+               char **ocsp_url, void **request_blob, size_t *request_size);
+
+int tls_ocsp_process_response(struct tls *ctx, const void *response_blob, 
size_t size);
+
 #ifdef __cplusplus
 }
 #endif
diff --git lib/libtls/tls_client.c lib/libtls/tls_client.c
index 3847f4c..86dd9a8 100644
--- lib/libtls/tls_client.c
+++ lib/libtls/tls_client.c
@@ -209,6 +209,11 @@ tls_connect_fds(struct tls *ctx, int fd_read, int fd_write,
            (tls_configure_ssl_verify(ctx, SSL_VERIFY_PEER) == -1))
                goto err;
 
+       if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, 
tls_ocsp_verify_callback) != 1) {
+               tls_set_errorx(ctx, "ssl OCSP verification setup failure");
+               goto err;
+       }
+
        if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
                tls_set_errorx(ctx, "ssl connection failure");
                goto err;
@@ -222,6 +227,10 @@ tls_connect_fds(struct tls *ctx, int fd_read, int fd_write,
                tls_set_errorx(ctx, "ssl file descriptor failure");
                goto err;
        }
+       if (SSL_set_tlsext_status_type(ctx->ssl_conn, TLSEXT_STATUSTYPE_ocsp) 
!= 1) {
+               tls_set_errorx(ctx, "ssl OCSP extension setup failure");
+               goto err;
+       }
 
        /*
         * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not
diff --git lib/libtls/tls_config.c lib/libtls/tls_config.c
index 8f73a5a..55655d6 100644
--- lib/libtls/tls_config.c
+++ lib/libtls/tls_config.c
@@ -371,6 +371,24 @@ tls_config_set_keypair_mem(struct tls_config *config, 
const uint8_t *cert,
        return (0);
 }
 
+int
+tls_config_set_ocsp_stapling_file(struct tls_config *config, const char 
*blob_file)
+{
+       if (blob_file != NULL)
+               tls_config_set_ocsp_stapling_mem(config, NULL, 0);
+
+       return set_string(&config->ocsp_file, blob_file);
+}
+
+int
+tls_config_set_ocsp_stapling_mem(struct tls_config *config, const uint8_t 
*blob, size_t len)
+{
+       if (blob != NULL)
+               tls_config_set_ocsp_stapling_file(config, NULL);
+
+       return set_mem(&config->ocsp_mem, &config->ocsp_len, blob, len);
+}
+
 void
 tls_config_set_protocols(struct tls_config *config, uint32_t protocols)
 {
diff --git lib/libtls/tls_internal.h lib/libtls/tls_internal.h
index 745fb40..9d53bc1 100644
--- lib/libtls/tls_internal.h
+++ lib/libtls/tls_internal.h
@@ -62,6 +62,9 @@ struct tls_config {
        int dheparams;
        int ecdhecurve;
        struct tls_keypair *keypair;
+       const char *ocsp_file;
+       char *ocsp_mem;
+       size_t ocsp_len;
        uint32_t protocols;
        int verify_cert;
        int verify_client;
@@ -85,10 +88,14 @@ struct tls_conninfo {
 #define TLS_CLIENT             (1 << 0)
 #define TLS_SERVER             (1 << 1)
 #define TLS_SERVER_CONN                (1 << 2)
+#define TLS_OCSP_CLIENT                (1 << 3)
 
 #define TLS_EOF_NO_CLOSE_NOTIFY        (1 << 0)
 #define TLS_HANDSHAKE_COMPLETE (1 << 1)
 
+struct tls_ocsp_query;
+struct tls_ocsp_info;
+
 struct tls {
        struct tls_config *config;
        struct tls_error error;
@@ -103,6 +110,20 @@ struct tls {
        SSL_CTX *ssl_ctx;
        X509 *ssl_peer_cert;
        struct tls_conninfo *conninfo;
+
+       const char *ocsp_result;
+       struct tls_ocsp_info *ocsp_info;
+
+       struct tls_ocsp_query *ocsp_query;
+};
+
+struct tls_ocsp_info {
+       int response_status;
+       int cert_status;
+       int crl_reason;
+       time_t this_update;
+       time_t next_update;
+       time_t revocation_time;
 };
 
 struct tls *tls_new(void);
@@ -143,6 +164,11 @@ int tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int 
ssl_ret,
 int tls_get_conninfo(struct tls *ctx);
 void tls_free_conninfo(struct tls_conninfo *conninfo);
 
+int tls_ocsp_verify_callback(SSL *ssl, void *arg);
+int tls_ocsp_stapling_callback(SSL *ssl, void *arg);
+void tls_ocsp_client_free(struct tls *ctx);
+void tls_ocsp_info_free(struct tls_ocsp_info *info);
+
 int asn1_time_parse(const char *, size_t, struct tm *, int);
 
 #endif /* HEADER_TLS_INTERNAL_H */
diff --git lib/libtls/tls_ocsp.c lib/libtls/tls_ocsp.c
new file mode 100644
index 0000000..8f8c1e3
--- /dev/null
+++ lib/libtls/tls_ocsp.c
@@ -0,0 +1,641 @@
+/*
+ * Copyright (c) 2015 Marko Kreen <mark...@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <openssl/err.h>
+#include <openssl/ocsp.h>
+#include <openssl/x509.h>
+
+#include <tls.h>
+#include "tls_internal.h"
+
+#define MAXAGE_SEC (14*24*60*60)
+#define JITTER_SEC (60)
+
+/*
+ * State for request.
+ */
+
+struct tls_ocsp_query {
+       /* responder location */
+       char *ocsp_url;
+
+       /* request blob */
+       uint8_t *request_data;
+       size_t request_size;
+
+       /* network state */
+       BIO *bio;
+       SSL_CTX *ssl_ctx;
+       OCSP_REQ_CTX *http_req;
+
+       /* cert data, this struct does not own these */
+       X509 *main_cert;
+       STACK_OF(X509) *extra_certs;
+       SSL_CTX *cert_ssl_ctx;
+};
+
+/*
+ * Extract OCSP response info.
+ */
+
+static int
+tls_asn1_parse_time(struct tls *ctx, ASN1_GENERALIZEDTIME *gt, time_t *dst_p)
+{
+       int res;
+       struct tm tm;
+
+       if (!gt) {
+               *dst_p = 0;
+               return 0;
+       }
+
+       res = asn1_time_parse(gt->data, gt->length, &tm, 0);
+       if (res == -1)
+               return -1;
+
+       res = 0;
+       *dst_p = timegm(&tm);
+       if (*dst_p == (time_t)-1)
+               res = -1;
+       return res;
+}
+
+static int
+tls_ocsp_fill_info(struct tls *ctx,
+       int response_status, int cert_status, int crl_reason,
+       ASN1_GENERALIZEDTIME *revtime,
+       ASN1_GENERALIZEDTIME *thisupd,
+       ASN1_GENERALIZEDTIME *nextupd)
+{
+       struct tls_ocsp_info *info;
+       int res;
+
+       info = calloc(1, sizeof (struct tls_ocsp_info));
+       if (!info) {
+               tls_set_error(ctx, "calloc");
+               return -1;
+       }
+       info->response_status = response_status;
+       info->cert_status = cert_status;
+       info->crl_reason = crl_reason;
+
+       res = tls_asn1_parse_time(ctx, revtime, &info->revocation_time);
+       if (res == 0)
+               res = tls_asn1_parse_time(ctx, thisupd, &info->this_update);
+       if (res == 0)
+               res = tls_asn1_parse_time(ctx, nextupd, &info->next_update);
+
+       if (res == 0) {
+               ctx->ocsp_info = info;
+       } else {
+               tls_ocsp_info_free(info);
+       }
+       return res;
+}
+
+static void
+tls_ocsp_fill_result(struct tls *ctx, int res)
+{
+       struct tls_ocsp_info *info = ctx->ocsp_info;
+       if (res < 0) {
+               ctx->ocsp_result = "error";
+       } else if (info->response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+               ctx->ocsp_result = 
OCSP_response_status_str(info->response_status);
+       } else if (info->cert_status != V_OCSP_CERTSTATUS_REVOKED) {
+               ctx->ocsp_result = OCSP_cert_status_str(info->cert_status);
+       } else {
+               ctx->ocsp_result = OCSP_crl_reason_str(info->crl_reason);
+       }
+}
+
+void
+tls_ocsp_info_free(struct tls_ocsp_info *info)
+{
+       free(info);
+}
+
+int
+tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status,
+                 int *crl_reason, time_t *this_update,
+                 time_t *next_update, time_t *revoction_time,
+                 const char **result_text)
+{
+       static const struct tls_ocsp_info no_ocsp = { -1, -1, -1, 0, 0, 0 };
+       const struct tls_ocsp_info *info = ctx->ocsp_info;
+       const char *ocsp_result = ctx->ocsp_result;
+       int ret = 0;
+
+       if (!info) {
+               info = &no_ocsp;
+               ret = -1;
+       }
+
+       if (!ocsp_result) {
+               ret = TLS_NO_OCSP;
+               ocsp_result = "no-ocsp";
+       }
+
+       if (response_status)
+               *response_status = info->response_status;
+       if (cert_status)
+               *cert_status = info->cert_status;
+       if (crl_reason)
+               *crl_reason = info->crl_reason;
+       if (this_update)
+               *this_update = info->this_update;
+       if (next_update)
+               *next_update = info->next_update;
+       if (revoction_time)
+               *revoction_time = info->revocation_time;
+       if (result_text)
+               *result_text = ocsp_result;
+
+       return ret;
+}
+
+/*
+ * Verify stapled response
+ */
+
+static OCSP_CERTID *
+tls_ocsp_get_certid(X509 *main_cert, STACK_OF(X509) *extra_certs, SSL_CTX 
*ssl_ctx)
+{
+       X509_NAME *issuer_name;
+       X509 *issuer;
+       X509_STORE_CTX storectx;
+       X509_OBJECT tmpobj;
+       OCSP_CERTID *cid = NULL;
+       X509_STORE *store;
+       int ok;
+
+       issuer_name = X509_get_issuer_name(main_cert);
+       if (!issuer_name)
+               return NULL;
+
+       if (extra_certs) {
+               issuer = X509_find_by_subject(extra_certs, issuer_name);
+               if (issuer)
+                       return OCSP_cert_to_id(NULL, main_cert, issuer);
+       }
+
+       store = SSL_CTX_get_cert_store(ssl_ctx);
+       if (!store)
+               return NULL;
+       ok = X509_STORE_CTX_init(&storectx, store, main_cert, extra_certs);
+       if (ok != 1)
+               return NULL;
+       ok = X509_STORE_get_by_subject(&storectx, X509_LU_X509, issuer_name, 
&tmpobj);
+       if (ok == 1) {
+               cid = OCSP_cert_to_id(NULL, main_cert, tmpobj.data.x509);
+               X509_free(tmpobj.data.x509);
+       }
+       X509_STORE_CTX_cleanup(&storectx);
+       return cid;
+}
+
+static int
+tls_ocsp_verify_response(struct tls *ctx, X509 *main_cert, STACK_OF(X509) 
*extra_certs,
+                        SSL_CTX *ssl_ctx, OCSP_RESPONSE *resp)
+{
+       OCSP_BASICRESP *br = NULL;
+       STACK_OF(X509) *ocsp_chain = NULL;
+       ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL;
+       OCSP_CERTID *cid = NULL;
+       STACK_OF(X509) *combined = NULL;
+       int response_status=0, cert_status=0, crl_reason=0;
+       int ssl_res, ret = -1;
+       unsigned long flags;
+
+       br = OCSP_response_get1_basic(resp);
+       if (!br) {
+               tls_set_errorx(ctx, "ocsp error: cannot load");
+               goto error;
+       }
+
+       /*
+        * Skip validation of 'extra_certs' as this should be done
+        * already as part of main handshake.
+        */
+       flags = OCSP_TRUSTOTHER;
+       ocsp_chain = extra_certs;
+
+       /* now verify */
+       ssl_res = OCSP_basic_verify(br, ocsp_chain, 
SSL_CTX_get_cert_store(ssl_ctx), flags);
+
+       if (ssl_res != 1) {
+               tls_set_error(ctx, "ocsp verify failed");
+               goto error;
+       }
+
+       /* signature OK, look inside */
+       response_status = OCSP_response_status(resp);
+       if (response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+               tls_set_errorx(ctx, "ocsp verify failed: unsuccessful response 
- %s",
+                              OCSP_response_status_str(response_status));
+               goto error;
+       }
+
+       cid = tls_ocsp_get_certid(main_cert, extra_certs, ssl_ctx);
+       if (!cid) {
+               tls_set_errorx(ctx, "ocsp verify failed: no issuer cert");
+               goto error;
+       }
+
+       ssl_res = OCSP_resp_find_status(br, cid, &cert_status, &crl_reason, 
&revtime, &thisupd, &nextupd);
+       if (ssl_res != 1) {
+               tls_set_errorx(ctx, "ocsp verify failed: no result for cert");
+               goto error;
+       }
+
+       ssl_res = OCSP_check_validity(thisupd, nextupd, JITTER_SEC, MAXAGE_SEC);
+       if (ssl_res != 1) {
+               tls_set_errorx(ctx, "ocsp verify failed: bad age");
+               goto error;
+       }
+
+       ssl_res = tls_ocsp_fill_info(ctx, response_status, cert_status, 
crl_reason, revtime, thisupd, nextupd);
+       if (ssl_res != 0)
+               goto error;
+
+       /* finally can look at status */
+       if (cert_status != V_OCSP_CERTSTATUS_GOOD && cert_status != 
V_OCSP_CERTSTATUS_UNKNOWN) {
+               tls_set_errorx(ctx, "ocsp verify failed: revoked cert - %s",
+                              OCSP_crl_reason_str(crl_reason));
+               goto error;
+       }
+
+       ret = 0;
+
+error:
+       sk_X509_free(combined);
+       OCSP_CERTID_free(cid);
+       OCSP_BASICRESP_free(br);
+       return ret;
+}
+
+/*
+ * Same callback on client-side has different error proto:
+ * 1=OK, 0=bad, -1=internal error
+ */
+
+int
+tls_ocsp_verify_callback(SSL *ssl, void *arg)
+{
+       OCSP_RESPONSE *resp = NULL;
+       STACK_OF(X509) *extra_certs = NULL;
+       X509 *peer = NULL;
+       const unsigned char *raw = NULL;
+       int size, res = -1;
+       struct tls *ctx;
+
+       ctx = SSL_get_app_data(ssl);
+       if (!ctx)
+               return -1;
+
+       size = SSL_get_tlsext_status_ocsp_resp(ssl, &raw);
+       if (size <= 0)
+               return 1;
+
+       peer = SSL_get_peer_certificate(ssl);
+       if (!peer) {
+               tls_set_errorx(ctx, "ocsp verify failed: no peer cert");
+               goto error;
+       }
+
+       resp = d2i_OCSP_RESPONSE(NULL, &raw, size);
+       if (!resp) {
+               tls_set_errorx(ctx, "ocsp verify failed: parse failed");
+               goto error;
+       }
+
+       extra_certs = SSL_get_peer_cert_chain(ssl);
+       res = tls_ocsp_verify_response(ctx, peer, extra_certs, ctx->ssl_ctx, 
resp);
+error:
+       tls_ocsp_fill_result(ctx, res);
+       OCSP_RESPONSE_free(resp);
+       X509_free(peer);
+       return (res == 0) ? 1 : 0;
+}
+
+/*
+ * Staple OCSP response to server handshake.
+ */
+
+int
+tls_ocsp_stapling_callback(SSL *ssl, void *arg)
+{
+       struct tls *ctx;
+       char *mem, *fmem = NULL;
+       uint8_t *xmem;
+       size_t len;
+       int ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+
+       ctx = SSL_get_app_data(ssl);
+       if (!ctx)
+               return SSL_TLSEXT_ERR_NOACK;
+
+       if (ctx->config->ocsp_file) {
+               fmem = mem = (char*)tls_load_file(ctx->config->ocsp_file, &len, 
NULL);
+               if (!mem)
+                       goto err;
+       } else {
+               mem = ctx->config->ocsp_mem;
+               len = ctx->config->ocsp_len;
+               if (!mem)
+                       return SSL_TLSEXT_ERR_NOACK;
+       }
+       xmem = malloc(len);
+       if (xmem) {
+               memcpy(xmem, mem, len);
+               if (SSL_set_tlsext_status_ocsp_resp(ctx->ssl_conn, xmem, len) 
!= 1) {
+                       free(xmem);
+                       goto err;
+               }
+               ret = SSL_TLSEXT_ERR_OK;
+       }
+err:
+       free(fmem);
+       return ret;
+}
+
+/*
+ * Query OCSP responder over HTTP(S).
+ */
+
+void
+tls_ocsp_client_free(struct tls *ctx)
+{
+       struct tls_ocsp_query *q;
+       if (!ctx)
+               return;
+       q = ctx->ocsp_query;
+       if (q) {
+               if (q->http_req)
+                       OCSP_REQ_CTX_free(q->http_req);
+               BIO_free_all(q->bio);
+               SSL_CTX_free(q->ssl_ctx);
+
+               free(q->ocsp_url);
+               free(q->request_data);
+               free(q);
+
+               ctx->ocsp_query = NULL;
+       }
+}
+
+static struct tls *
+tls_ocsp_client_new(void)
+{
+       struct tls *ctx;
+
+       ctx = tls_new();
+       if (!ctx)
+               return NULL;
+       ctx->flags = TLS_OCSP_CLIENT;
+
+       ctx->ocsp_query = calloc(1, sizeof (struct tls_ocsp_query));
+       if (!ctx->ocsp_query) {
+               tls_free(ctx);
+               return NULL;
+       }
+       return ctx;
+}
+
+static int
+tls_build_ocsp_request(struct tls *ctx)
+{
+       struct tls_ocsp_query *q;
+       int ok, ret = -1;
+       OCSP_REQUEST *req = NULL;
+       OCSP_CERTID *cid = NULL;
+       OCSP_ONEREQ *onereq = NULL;
+       BIO *mem = NULL;
+       void *data;
+
+       q = ctx->ocsp_query;
+
+       cid = tls_ocsp_get_certid(q->main_cert, q->extra_certs, 
q->cert_ssl_ctx);
+       if (!cid) {
+               tls_set_errorx(ctx, "Cannot create cert-id");
+               goto failed;
+       }
+
+       req = OCSP_REQUEST_new();
+       if (!req) {
+               tls_set_error(ctx, "Cannot create request");
+               goto failed;
+       }
+
+       onereq = OCSP_request_add0_id(req, cid);
+       if (!onereq) {
+               tls_set_error(ctx, "Cannot add cert-id to request");
+               goto failed;
+       }
+       cid = NULL;
+
+       /*
+        * Now render it.
+        */
+
+       mem = BIO_new(BIO_s_mem());
+       if (!mem) {
+               tls_set_errorx(ctx, "BIO_new");
+               goto failed;
+       }
+
+       ok = i2d_OCSP_REQUEST_bio(mem, req);
+       if (!ok) {
+               tls_set_error(ctx, "i2d_OCSP_RESPONSE_bio");
+               goto failed;
+       }
+       q->request_size = BIO_get_mem_data(mem, &data);
+       q->request_data = malloc(q->request_size);
+       if (!q->request_data) {
+               tls_set_error(ctx, "Failed to allocate request data");
+               goto failed;
+       }
+       memcpy(q->request_data, data, q->request_size);
+
+       req = NULL;
+       ret = 0;
+failed:
+       OCSP_CERTID_free(cid);
+       OCSP_REQUEST_free(req);
+       BIO_free(mem);
+       return ret;
+}
+
+static int
+tls_ocsp_setup(struct tls **ocsp_ctx_p, struct tls_config *config, struct tls 
*target)
+{
+       struct tls *ctx;
+       struct tls_ocsp_query *q;
+       int ret;
+       STACK_OF(OPENSSL_STRING) *ocsp_urls;
+
+       ctx = tls_ocsp_client_new();
+       if (!ctx)
+               return -1;
+
+       *ocsp_ctx_p = ctx;
+       q = ctx->ocsp_query;
+
+       if (config) {
+               /* create ctx->ssl_ctx */
+               ctx->flags = TLS_SERVER;
+               ret = tls_configure(ctx, config);
+               ctx->flags = TLS_OCSP_CLIENT;
+               if (ret != 0)
+                       return ret;
+
+               q->main_cert = SSL_get_certificate(ctx->ssl_conn);
+               q->cert_ssl_ctx = ctx->ssl_ctx;
+               SSL_CTX_get_extra_chain_certs(ctx->ssl_ctx, &q->extra_certs);
+       } else {
+               /* steal state from target struct */
+               q->main_cert = SSL_get_peer_certificate(target->ssl_conn);
+               q->extra_certs = SSL_get_peer_cert_chain(target->ssl_conn);
+               q->cert_ssl_ctx = target->ssl_ctx;
+               X509_free(q->main_cert); /* unref */
+       }
+
+       if (!q->main_cert) {
+               tls_set_errorx(ctx, "No cert");
+               return -1;
+       }
+
+       ocsp_urls = X509_get1_ocsp(q->main_cert);
+       if (!ocsp_urls)
+               return TLS_NO_OCSP;
+       q->ocsp_url = strdup(sk_OPENSSL_STRING_value(ocsp_urls, 0));
+       if (!q->ocsp_url) {
+               tls_set_errorx(ctx, "Cannot copy URL");
+               goto failed;
+       }
+
+       ret = tls_build_ocsp_request(ctx);
+       if (ret != 0)
+               goto failed;
+
+       *ocsp_ctx_p = ctx;
+
+failed:
+       X509_email_free(ocsp_urls);
+       return ret;
+}
+
+static int
+tls_ocsp_process_response_parsed(struct tls *ctx, struct tls_config *config, 
OCSP_RESPONSE *resp)
+{
+       struct tls_ocsp_query *q = ctx->ocsp_query;
+       BIO *mem = NULL;
+       size_t len;
+       unsigned char *data;
+       int ret = -1, ok, res;
+
+       res = tls_ocsp_verify_response(ctx, q->main_cert, q->extra_certs, 
q->cert_ssl_ctx, resp);
+       if (res < 0)
+               goto failed;
+
+       /* Update blob in config */
+       if (config) {
+               mem = BIO_new(BIO_s_mem());
+               if (!mem) {
+                       tls_set_error(ctx, "BIO_new");
+                       goto failed;
+               }
+               ok = i2d_OCSP_RESPONSE_bio(mem, resp);
+               if (!ok) {
+                       tls_set_error(ctx, "i2d_OCSP_RESPONSE_bio");
+                       goto failed;
+               }
+               len = BIO_get_mem_data(mem, &data);
+               res = tls_config_set_ocsp_stapling_mem(config, data, len);
+               if (res < 0)
+                       goto failed;
+       }
+       ret = 0;
+failed:
+       BIO_free(mem);
+       tls_ocsp_fill_result(ctx, ret);
+       return ret;
+}
+
+static int
+tls_ocsp_create_request(struct tls **ocsp_ctx_p,
+                       struct tls_config *config, struct tls *target,
+                       char **ocsp_url,
+                       void **request_blob, size_t *request_size)
+{
+       int res;
+       struct tls_ocsp_query *q;
+
+       res = tls_ocsp_setup(ocsp_ctx_p, config, target);
+       if (res != 0)
+               return res;
+       q = (*ocsp_ctx_p)->ocsp_query;
+
+       *ocsp_url = q->ocsp_url;
+       *request_blob = q->request_data;
+       *request_size = q->request_size;
+
+       return 0;
+}
+
+/*
+ * Public API for request blobs.
+ */
+
+int
+tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target,
+                           char **ocsp_url, void **request_blob, size_t 
*request_size)
+{
+       return tls_ocsp_create_request(ocsp_ctx_p, NULL, target,
+                       ocsp_url, request_blob, request_size);
+}
+
+int
+tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p,
+               struct tls_config *config,
+               char **ocsp_url, void **request_blob, size_t *request_size)
+{
+       return tls_ocsp_create_request(ocsp_ctx_p, config, NULL,
+                       ocsp_url, request_blob, request_size);
+}
+
+int
+tls_ocsp_process_response(struct tls *ctx, const void *response_blob, size_t 
size)
+{
+       int ret;
+       OCSP_RESPONSE *resp;
+       const unsigned char *raw = response_blob;
+
+       resp = d2i_OCSP_RESPONSE(NULL, &raw, size);
+       if (!resp) {
+               ctx->ocsp_result = "parse-failed";
+               tls_set_error(ctx, "parse failed");
+               return -1;
+       }
+       ret = tls_ocsp_process_response_parsed(ctx, ctx->config, resp);
+       OCSP_RESPONSE_free(resp);
+       return ret;
+}
+
diff --git lib/libtls/tls_server.c lib/libtls/tls_server.c
index 1d94c99..2d383d2 100644
--- lib/libtls/tls_server.c
+++ lib/libtls/tls_server.c
@@ -93,6 +93,11 @@ tls_configure_server(struct tls *ctx)
                SSL_CTX_set_options(ctx->ssl_ctx,
                    SSL_OP_CIPHER_SERVER_PREFERENCE);
 
+       if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, 
tls_ocsp_stapling_callback) != 1) {
+               tls_set_errorx(ctx, "ssl OCSP stapling setup failure");
+               goto err;
+       }
+
        /*
         * Set session ID context to a random value.  We don't support
         * persistent caching of sessions so it is OK to set a temporary




Attachment: ocsp-connect.tgz
Description: application/tar-gz

diff --git lib/libtls/Makefile lib/libtls/Makefile
index ca2f00b..461bf44 100644
--- lib/libtls/Makefile
+++ lib/libtls/Makefile
@@ -19,6 +19,7 @@ SRCS= tls.c \
        tls_peer.c \
        tls_server.c \
        tls_util.c \
+       tls_ocsp.c \
        tls_verify.c
 
 MAN=   tls_init.3
diff --git lib/libtls/tls.c lib/libtls/tls.c
index 76d00e5..b00bea8 100644
--- lib/libtls/tls.c
+++ lib/libtls/tls.c
@@ -393,6 +393,13 @@ tls_reset(struct tls *ctx)
        tls_free_conninfo(ctx->conninfo);
        free(ctx->conninfo);
        ctx->conninfo = NULL;
+
+       tls_ocsp_info_free(ctx->ocsp_info);
+       ctx->ocsp_info = NULL;
+       ctx->ocsp_result = NULL;
+
+       if (ctx->flags & TLS_OCSP_CLIENT)
+               tls_ocsp_client_free(ctx);
 }
 
 int
diff --git lib/libtls/tls.h lib/libtls/tls.h
index 75c46c1..da6cd69 100644
--- lib/libtls/tls.h
+++ lib/libtls/tls.h
@@ -40,6 +40,29 @@ extern "C" {
 
 #define TLS_WANT_POLLIN                -2
 #define TLS_WANT_POLLOUT       -3
+#define TLS_NO_OCSP            -4
+
+#define TLS_OCSP_RESPONSE_SUCCESSFUL           0
+#define TLS_OCSP_RESPONSE_MALFORMED            1
+#define TLS_OCSP_RESPONSE_INTERNALERR          2
+#define TLS_OCSP_RESPONSE_TRYLATER             3
+#define TLS_OCSP_RESPONSE_SIGREQUIRED          5
+#define TLS_OCSP_RESPONSE_UNAUTHORIZED         6
+
+#define TLS_OCSP_CERT_GOOD                     0
+#define TLS_OCSP_CERT_REVOKED                  1
+#define TLS_OCSP_CERT_UNKNOWN                  2
+
+#define TLS_CRL_REASON_UNPSECIFIED             0
+#define TLS_CRL_REASON_KEY_COMPROMISE          1
+#define TLS_CRL_REASON_CA_COMPROMISE           2
+#define TLS_CRL_REASON_AFFILIATION_CHANGED     3
+#define TLS_CRL_REASON_SUPERSEDED              4
+#define TLS_CRL_REASON_CESSATION_OF_OPERATION  5
+#define TLS_CRL_REASON_CERTIFICATE_HOLD                6
+#define TLS_CRL_REASON_REMOVE_FROM_CRL         8
+#define TLS_CRL_REASON_PRIVILEGE_WITH_DRAWN    9
+#define TLS_CRL_REASON_AA_COMPROMISE           10
 
 struct tls;
 struct tls_config;
@@ -70,6 +93,8 @@ int tls_config_set_keypair_file(struct tls_config *_config,
     const char *_cert_file, const char *_key_file);
 int tls_config_set_keypair_mem(struct tls_config *_config, const uint8_t 
*_cert,
     size_t _cert_len, const uint8_t *_key, size_t _key_len);
+int tls_config_set_ocsp_stapling_file(struct tls_config *_config, const char 
*_blob_file);
+int tls_config_set_ocsp_stapling_mem(struct tls_config *_config, const uint8_t 
*_blob, size_t _len);
 void tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols);
 void tls_config_set_verify_depth(struct tls_config *_config, int 
_verify_depth);
 
@@ -121,6 +146,18 @@ const char *tls_conn_cipher(struct tls *_ctx);
 
 uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password);
 
+int tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status, 
int *crl_reason,
+                     time_t *this_update, time_t *next_update, time_t 
*revoction_time,
+                     const char **result_text);
+
+int tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target,
+                           char **ocsp_url, void **request_blob, size_t 
*request_size);
+
+int tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p, struct 
tls_config *config,
+               char **ocsp_url, void **request_blob, size_t *request_size);
+
+int tls_ocsp_process_response(struct tls *ctx, const void *response_blob, 
size_t size);
+
 #ifdef __cplusplus
 }
 #endif
diff --git lib/libtls/tls_client.c lib/libtls/tls_client.c
index 3847f4c..86dd9a8 100644
--- lib/libtls/tls_client.c
+++ lib/libtls/tls_client.c
@@ -209,6 +209,11 @@ tls_connect_fds(struct tls *ctx, int fd_read, int fd_write,
            (tls_configure_ssl_verify(ctx, SSL_VERIFY_PEER) == -1))
                goto err;
 
+       if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, 
tls_ocsp_verify_callback) != 1) {
+               tls_set_errorx(ctx, "ssl OCSP verification setup failure");
+               goto err;
+       }
+
        if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
                tls_set_errorx(ctx, "ssl connection failure");
                goto err;
@@ -222,6 +227,10 @@ tls_connect_fds(struct tls *ctx, int fd_read, int fd_write,
                tls_set_errorx(ctx, "ssl file descriptor failure");
                goto err;
        }
+       if (SSL_set_tlsext_status_type(ctx->ssl_conn, TLSEXT_STATUSTYPE_ocsp) 
!= 1) {
+               tls_set_errorx(ctx, "ssl OCSP extension setup failure");
+               goto err;
+       }
 
        /*
         * RFC4366 (SNI): Literal IPv4 and IPv6 addresses are not
diff --git lib/libtls/tls_config.c lib/libtls/tls_config.c
index 8f73a5a..55655d6 100644
--- lib/libtls/tls_config.c
+++ lib/libtls/tls_config.c
@@ -371,6 +371,24 @@ tls_config_set_keypair_mem(struct tls_config *config, 
const uint8_t *cert,
        return (0);
 }
 
+int
+tls_config_set_ocsp_stapling_file(struct tls_config *config, const char 
*blob_file)
+{
+       if (blob_file != NULL)
+               tls_config_set_ocsp_stapling_mem(config, NULL, 0);
+
+       return set_string(&config->ocsp_file, blob_file);
+}
+
+int
+tls_config_set_ocsp_stapling_mem(struct tls_config *config, const uint8_t 
*blob, size_t len)
+{
+       if (blob != NULL)
+               tls_config_set_ocsp_stapling_file(config, NULL);
+
+       return set_mem(&config->ocsp_mem, &config->ocsp_len, blob, len);
+}
+
 void
 tls_config_set_protocols(struct tls_config *config, uint32_t protocols)
 {
diff --git lib/libtls/tls_internal.h lib/libtls/tls_internal.h
index 745fb40..9d53bc1 100644
--- lib/libtls/tls_internal.h
+++ lib/libtls/tls_internal.h
@@ -62,6 +62,9 @@ struct tls_config {
        int dheparams;
        int ecdhecurve;
        struct tls_keypair *keypair;
+       const char *ocsp_file;
+       char *ocsp_mem;
+       size_t ocsp_len;
        uint32_t protocols;
        int verify_cert;
        int verify_client;
@@ -85,10 +88,14 @@ struct tls_conninfo {
 #define TLS_CLIENT             (1 << 0)
 #define TLS_SERVER             (1 << 1)
 #define TLS_SERVER_CONN                (1 << 2)
+#define TLS_OCSP_CLIENT                (1 << 3)
 
 #define TLS_EOF_NO_CLOSE_NOTIFY        (1 << 0)
 #define TLS_HANDSHAKE_COMPLETE (1 << 1)
 
+struct tls_ocsp_query;
+struct tls_ocsp_info;
+
 struct tls {
        struct tls_config *config;
        struct tls_error error;
@@ -103,6 +110,20 @@ struct tls {
        SSL_CTX *ssl_ctx;
        X509 *ssl_peer_cert;
        struct tls_conninfo *conninfo;
+
+       const char *ocsp_result;
+       struct tls_ocsp_info *ocsp_info;
+
+       struct tls_ocsp_query *ocsp_query;
+};
+
+struct tls_ocsp_info {
+       int response_status;
+       int cert_status;
+       int crl_reason;
+       time_t this_update;
+       time_t next_update;
+       time_t revocation_time;
 };
 
 struct tls *tls_new(void);
@@ -143,6 +164,11 @@ int tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int 
ssl_ret,
 int tls_get_conninfo(struct tls *ctx);
 void tls_free_conninfo(struct tls_conninfo *conninfo);
 
+int tls_ocsp_verify_callback(SSL *ssl, void *arg);
+int tls_ocsp_stapling_callback(SSL *ssl, void *arg);
+void tls_ocsp_client_free(struct tls *ctx);
+void tls_ocsp_info_free(struct tls_ocsp_info *info);
+
 int asn1_time_parse(const char *, size_t, struct tm *, int);
 
 #endif /* HEADER_TLS_INTERNAL_H */
diff --git lib/libtls/tls_ocsp.c lib/libtls/tls_ocsp.c
new file mode 100644
index 0000000..8f8c1e3
--- /dev/null
+++ lib/libtls/tls_ocsp.c
@@ -0,0 +1,641 @@
+/*
+ * Copyright (c) 2015 Marko Kreen <mark...@gmail.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <openssl/err.h>
+#include <openssl/ocsp.h>
+#include <openssl/x509.h>
+
+#include <tls.h>
+#include "tls_internal.h"
+
+#define MAXAGE_SEC (14*24*60*60)
+#define JITTER_SEC (60)
+
+/*
+ * State for request.
+ */
+
+struct tls_ocsp_query {
+       /* responder location */
+       char *ocsp_url;
+
+       /* request blob */
+       uint8_t *request_data;
+       size_t request_size;
+
+       /* network state */
+       BIO *bio;
+       SSL_CTX *ssl_ctx;
+       OCSP_REQ_CTX *http_req;
+
+       /* cert data, this struct does not own these */
+       X509 *main_cert;
+       STACK_OF(X509) *extra_certs;
+       SSL_CTX *cert_ssl_ctx;
+};
+
+/*
+ * Extract OCSP response info.
+ */
+
+static int
+tls_asn1_parse_time(struct tls *ctx, ASN1_GENERALIZEDTIME *gt, time_t *dst_p)
+{
+       int res;
+       struct tm tm;
+
+       if (!gt) {
+               *dst_p = 0;
+               return 0;
+       }
+
+       res = asn1_time_parse(gt->data, gt->length, &tm, 0);
+       if (res == -1)
+               return -1;
+
+       res = 0;
+       *dst_p = timegm(&tm);
+       if (*dst_p == (time_t)-1)
+               res = -1;
+       return res;
+}
+
+static int
+tls_ocsp_fill_info(struct tls *ctx,
+       int response_status, int cert_status, int crl_reason,
+       ASN1_GENERALIZEDTIME *revtime,
+       ASN1_GENERALIZEDTIME *thisupd,
+       ASN1_GENERALIZEDTIME *nextupd)
+{
+       struct tls_ocsp_info *info;
+       int res;
+
+       info = calloc(1, sizeof (struct tls_ocsp_info));
+       if (!info) {
+               tls_set_error(ctx, "calloc");
+               return -1;
+       }
+       info->response_status = response_status;
+       info->cert_status = cert_status;
+       info->crl_reason = crl_reason;
+
+       res = tls_asn1_parse_time(ctx, revtime, &info->revocation_time);
+       if (res == 0)
+               res = tls_asn1_parse_time(ctx, thisupd, &info->this_update);
+       if (res == 0)
+               res = tls_asn1_parse_time(ctx, nextupd, &info->next_update);
+
+       if (res == 0) {
+               ctx->ocsp_info = info;
+       } else {
+               tls_ocsp_info_free(info);
+       }
+       return res;
+}
+
+static void
+tls_ocsp_fill_result(struct tls *ctx, int res)
+{
+       struct tls_ocsp_info *info = ctx->ocsp_info;
+       if (res < 0) {
+               ctx->ocsp_result = "error";
+       } else if (info->response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+               ctx->ocsp_result = 
OCSP_response_status_str(info->response_status);
+       } else if (info->cert_status != V_OCSP_CERTSTATUS_REVOKED) {
+               ctx->ocsp_result = OCSP_cert_status_str(info->cert_status);
+       } else {
+               ctx->ocsp_result = OCSP_crl_reason_str(info->crl_reason);
+       }
+}
+
+void
+tls_ocsp_info_free(struct tls_ocsp_info *info)
+{
+       free(info);
+}
+
+int
+tls_get_ocsp_info(struct tls *ctx, int *response_status, int *cert_status,
+                 int *crl_reason, time_t *this_update,
+                 time_t *next_update, time_t *revoction_time,
+                 const char **result_text)
+{
+       static const struct tls_ocsp_info no_ocsp = { -1, -1, -1, 0, 0, 0 };
+       const struct tls_ocsp_info *info = ctx->ocsp_info;
+       const char *ocsp_result = ctx->ocsp_result;
+       int ret = 0;
+
+       if (!info) {
+               info = &no_ocsp;
+               ret = -1;
+       }
+
+       if (!ocsp_result) {
+               ret = TLS_NO_OCSP;
+               ocsp_result = "no-ocsp";
+       }
+
+       if (response_status)
+               *response_status = info->response_status;
+       if (cert_status)
+               *cert_status = info->cert_status;
+       if (crl_reason)
+               *crl_reason = info->crl_reason;
+       if (this_update)
+               *this_update = info->this_update;
+       if (next_update)
+               *next_update = info->next_update;
+       if (revoction_time)
+               *revoction_time = info->revocation_time;
+       if (result_text)
+               *result_text = ocsp_result;
+
+       return ret;
+}
+
+/*
+ * Verify stapled response
+ */
+
+static OCSP_CERTID *
+tls_ocsp_get_certid(X509 *main_cert, STACK_OF(X509) *extra_certs, SSL_CTX 
*ssl_ctx)
+{
+       X509_NAME *issuer_name;
+       X509 *issuer;
+       X509_STORE_CTX storectx;
+       X509_OBJECT tmpobj;
+       OCSP_CERTID *cid = NULL;
+       X509_STORE *store;
+       int ok;
+
+       issuer_name = X509_get_issuer_name(main_cert);
+       if (!issuer_name)
+               return NULL;
+
+       if (extra_certs) {
+               issuer = X509_find_by_subject(extra_certs, issuer_name);
+               if (issuer)
+                       return OCSP_cert_to_id(NULL, main_cert, issuer);
+       }
+
+       store = SSL_CTX_get_cert_store(ssl_ctx);
+       if (!store)
+               return NULL;
+       ok = X509_STORE_CTX_init(&storectx, store, main_cert, extra_certs);
+       if (ok != 1)
+               return NULL;
+       ok = X509_STORE_get_by_subject(&storectx, X509_LU_X509, issuer_name, 
&tmpobj);
+       if (ok == 1) {
+               cid = OCSP_cert_to_id(NULL, main_cert, tmpobj.data.x509);
+               X509_free(tmpobj.data.x509);
+       }
+       X509_STORE_CTX_cleanup(&storectx);
+       return cid;
+}
+
+static int
+tls_ocsp_verify_response(struct tls *ctx, X509 *main_cert, STACK_OF(X509) 
*extra_certs,
+                        SSL_CTX *ssl_ctx, OCSP_RESPONSE *resp)
+{
+       OCSP_BASICRESP *br = NULL;
+       STACK_OF(X509) *ocsp_chain = NULL;
+       ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL;
+       OCSP_CERTID *cid = NULL;
+       STACK_OF(X509) *combined = NULL;
+       int response_status=0, cert_status=0, crl_reason=0;
+       int ssl_res, ret = -1;
+       unsigned long flags;
+
+       br = OCSP_response_get1_basic(resp);
+       if (!br) {
+               tls_set_errorx(ctx, "ocsp error: cannot load");
+               goto error;
+       }
+
+       /*
+        * Skip validation of 'extra_certs' as this should be done
+        * already as part of main handshake.
+        */
+       flags = OCSP_TRUSTOTHER;
+       ocsp_chain = extra_certs;
+
+       /* now verify */
+       ssl_res = OCSP_basic_verify(br, ocsp_chain, 
SSL_CTX_get_cert_store(ssl_ctx), flags);
+
+       if (ssl_res != 1) {
+               tls_set_error(ctx, "ocsp verify failed");
+               goto error;
+       }
+
+       /* signature OK, look inside */
+       response_status = OCSP_response_status(resp);
+       if (response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+               tls_set_errorx(ctx, "ocsp verify failed: unsuccessful response 
- %s",
+                              OCSP_response_status_str(response_status));
+               goto error;
+       }
+
+       cid = tls_ocsp_get_certid(main_cert, extra_certs, ssl_ctx);
+       if (!cid) {
+               tls_set_errorx(ctx, "ocsp verify failed: no issuer cert");
+               goto error;
+       }
+
+       ssl_res = OCSP_resp_find_status(br, cid, &cert_status, &crl_reason, 
&revtime, &thisupd, &nextupd);
+       if (ssl_res != 1) {
+               tls_set_errorx(ctx, "ocsp verify failed: no result for cert");
+               goto error;
+       }
+
+       ssl_res = OCSP_check_validity(thisupd, nextupd, JITTER_SEC, MAXAGE_SEC);
+       if (ssl_res != 1) {
+               tls_set_errorx(ctx, "ocsp verify failed: bad age");
+               goto error;
+       }
+
+       ssl_res = tls_ocsp_fill_info(ctx, response_status, cert_status, 
crl_reason, revtime, thisupd, nextupd);
+       if (ssl_res != 0)
+               goto error;
+
+       /* finally can look at status */
+       if (cert_status != V_OCSP_CERTSTATUS_GOOD && cert_status != 
V_OCSP_CERTSTATUS_UNKNOWN) {
+               tls_set_errorx(ctx, "ocsp verify failed: revoked cert - %s",
+                              OCSP_crl_reason_str(crl_reason));
+               goto error;
+       }
+
+       ret = 0;
+
+error:
+       sk_X509_free(combined);
+       OCSP_CERTID_free(cid);
+       OCSP_BASICRESP_free(br);
+       return ret;
+}
+
+/*
+ * Same callback on client-side has different error proto:
+ * 1=OK, 0=bad, -1=internal error
+ */
+
+int
+tls_ocsp_verify_callback(SSL *ssl, void *arg)
+{
+       OCSP_RESPONSE *resp = NULL;
+       STACK_OF(X509) *extra_certs = NULL;
+       X509 *peer = NULL;
+       const unsigned char *raw = NULL;
+       int size, res = -1;
+       struct tls *ctx;
+
+       ctx = SSL_get_app_data(ssl);
+       if (!ctx)
+               return -1;
+
+       size = SSL_get_tlsext_status_ocsp_resp(ssl, &raw);
+       if (size <= 0)
+               return 1;
+
+       peer = SSL_get_peer_certificate(ssl);
+       if (!peer) {
+               tls_set_errorx(ctx, "ocsp verify failed: no peer cert");
+               goto error;
+       }
+
+       resp = d2i_OCSP_RESPONSE(NULL, &raw, size);
+       if (!resp) {
+               tls_set_errorx(ctx, "ocsp verify failed: parse failed");
+               goto error;
+       }
+
+       extra_certs = SSL_get_peer_cert_chain(ssl);
+       res = tls_ocsp_verify_response(ctx, peer, extra_certs, ctx->ssl_ctx, 
resp);
+error:
+       tls_ocsp_fill_result(ctx, res);
+       OCSP_RESPONSE_free(resp);
+       X509_free(peer);
+       return (res == 0) ? 1 : 0;
+}
+
+/*
+ * Staple OCSP response to server handshake.
+ */
+
+int
+tls_ocsp_stapling_callback(SSL *ssl, void *arg)
+{
+       struct tls *ctx;
+       char *mem, *fmem = NULL;
+       uint8_t *xmem;
+       size_t len;
+       int ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+
+       ctx = SSL_get_app_data(ssl);
+       if (!ctx)
+               return SSL_TLSEXT_ERR_NOACK;
+
+       if (ctx->config->ocsp_file) {
+               fmem = mem = (char*)tls_load_file(ctx->config->ocsp_file, &len, 
NULL);
+               if (!mem)
+                       goto err;
+       } else {
+               mem = ctx->config->ocsp_mem;
+               len = ctx->config->ocsp_len;
+               if (!mem)
+                       return SSL_TLSEXT_ERR_NOACK;
+       }
+       xmem = malloc(len);
+       if (xmem) {
+               memcpy(xmem, mem, len);
+               if (SSL_set_tlsext_status_ocsp_resp(ctx->ssl_conn, xmem, len) 
!= 1) {
+                       free(xmem);
+                       goto err;
+               }
+               ret = SSL_TLSEXT_ERR_OK;
+       }
+err:
+       free(fmem);
+       return ret;
+}
+
+/*
+ * Query OCSP responder over HTTP(S).
+ */
+
+void
+tls_ocsp_client_free(struct tls *ctx)
+{
+       struct tls_ocsp_query *q;
+       if (!ctx)
+               return;
+       q = ctx->ocsp_query;
+       if (q) {
+               if (q->http_req)
+                       OCSP_REQ_CTX_free(q->http_req);
+               BIO_free_all(q->bio);
+               SSL_CTX_free(q->ssl_ctx);
+
+               free(q->ocsp_url);
+               free(q->request_data);
+               free(q);
+
+               ctx->ocsp_query = NULL;
+       }
+}
+
+static struct tls *
+tls_ocsp_client_new(void)
+{
+       struct tls *ctx;
+
+       ctx = tls_new();
+       if (!ctx)
+               return NULL;
+       ctx->flags = TLS_OCSP_CLIENT;
+
+       ctx->ocsp_query = calloc(1, sizeof (struct tls_ocsp_query));
+       if (!ctx->ocsp_query) {
+               tls_free(ctx);
+               return NULL;
+       }
+       return ctx;
+}
+
+static int
+tls_build_ocsp_request(struct tls *ctx)
+{
+       struct tls_ocsp_query *q;
+       int ok, ret = -1;
+       OCSP_REQUEST *req = NULL;
+       OCSP_CERTID *cid = NULL;
+       OCSP_ONEREQ *onereq = NULL;
+       BIO *mem = NULL;
+       void *data;
+
+       q = ctx->ocsp_query;
+
+       cid = tls_ocsp_get_certid(q->main_cert, q->extra_certs, 
q->cert_ssl_ctx);
+       if (!cid) {
+               tls_set_errorx(ctx, "Cannot create cert-id");
+               goto failed;
+       }
+
+       req = OCSP_REQUEST_new();
+       if (!req) {
+               tls_set_error(ctx, "Cannot create request");
+               goto failed;
+       }
+
+       onereq = OCSP_request_add0_id(req, cid);
+       if (!onereq) {
+               tls_set_error(ctx, "Cannot add cert-id to request");
+               goto failed;
+       }
+       cid = NULL;
+
+       /*
+        * Now render it.
+        */
+
+       mem = BIO_new(BIO_s_mem());
+       if (!mem) {
+               tls_set_errorx(ctx, "BIO_new");
+               goto failed;
+       }
+
+       ok = i2d_OCSP_REQUEST_bio(mem, req);
+       if (!ok) {
+               tls_set_error(ctx, "i2d_OCSP_RESPONSE_bio");
+               goto failed;
+       }
+       q->request_size = BIO_get_mem_data(mem, &data);
+       q->request_data = malloc(q->request_size);
+       if (!q->request_data) {
+               tls_set_error(ctx, "Failed to allocate request data");
+               goto failed;
+       }
+       memcpy(q->request_data, data, q->request_size);
+
+       req = NULL;
+       ret = 0;
+failed:
+       OCSP_CERTID_free(cid);
+       OCSP_REQUEST_free(req);
+       BIO_free(mem);
+       return ret;
+}
+
+static int
+tls_ocsp_setup(struct tls **ocsp_ctx_p, struct tls_config *config, struct tls 
*target)
+{
+       struct tls *ctx;
+       struct tls_ocsp_query *q;
+       int ret;
+       STACK_OF(OPENSSL_STRING) *ocsp_urls;
+
+       ctx = tls_ocsp_client_new();
+       if (!ctx)
+               return -1;
+
+       *ocsp_ctx_p = ctx;
+       q = ctx->ocsp_query;
+
+       if (config) {
+               /* create ctx->ssl_ctx */
+               ctx->flags = TLS_SERVER;
+               ret = tls_configure(ctx, config);
+               ctx->flags = TLS_OCSP_CLIENT;
+               if (ret != 0)
+                       return ret;
+
+               q->main_cert = SSL_get_certificate(ctx->ssl_conn);
+               q->cert_ssl_ctx = ctx->ssl_ctx;
+               SSL_CTX_get_extra_chain_certs(ctx->ssl_ctx, &q->extra_certs);
+       } else {
+               /* steal state from target struct */
+               q->main_cert = SSL_get_peer_certificate(target->ssl_conn);
+               q->extra_certs = SSL_get_peer_cert_chain(target->ssl_conn);
+               q->cert_ssl_ctx = target->ssl_ctx;
+               X509_free(q->main_cert); /* unref */
+       }
+
+       if (!q->main_cert) {
+               tls_set_errorx(ctx, "No cert");
+               return -1;
+       }
+
+       ocsp_urls = X509_get1_ocsp(q->main_cert);
+       if (!ocsp_urls)
+               return TLS_NO_OCSP;
+       q->ocsp_url = strdup(sk_OPENSSL_STRING_value(ocsp_urls, 0));
+       if (!q->ocsp_url) {
+               tls_set_errorx(ctx, "Cannot copy URL");
+               goto failed;
+       }
+
+       ret = tls_build_ocsp_request(ctx);
+       if (ret != 0)
+               goto failed;
+
+       *ocsp_ctx_p = ctx;
+
+failed:
+       X509_email_free(ocsp_urls);
+       return ret;
+}
+
+static int
+tls_ocsp_process_response_parsed(struct tls *ctx, struct tls_config *config, 
OCSP_RESPONSE *resp)
+{
+       struct tls_ocsp_query *q = ctx->ocsp_query;
+       BIO *mem = NULL;
+       size_t len;
+       unsigned char *data;
+       int ret = -1, ok, res;
+
+       res = tls_ocsp_verify_response(ctx, q->main_cert, q->extra_certs, 
q->cert_ssl_ctx, resp);
+       if (res < 0)
+               goto failed;
+
+       /* Update blob in config */
+       if (config) {
+               mem = BIO_new(BIO_s_mem());
+               if (!mem) {
+                       tls_set_error(ctx, "BIO_new");
+                       goto failed;
+               }
+               ok = i2d_OCSP_RESPONSE_bio(mem, resp);
+               if (!ok) {
+                       tls_set_error(ctx, "i2d_OCSP_RESPONSE_bio");
+                       goto failed;
+               }
+               len = BIO_get_mem_data(mem, &data);
+               res = tls_config_set_ocsp_stapling_mem(config, data, len);
+               if (res < 0)
+                       goto failed;
+       }
+       ret = 0;
+failed:
+       BIO_free(mem);
+       tls_ocsp_fill_result(ctx, ret);
+       return ret;
+}
+
+static int
+tls_ocsp_create_request(struct tls **ocsp_ctx_p,
+                       struct tls_config *config, struct tls *target,
+                       char **ocsp_url,
+                       void **request_blob, size_t *request_size)
+{
+       int res;
+       struct tls_ocsp_query *q;
+
+       res = tls_ocsp_setup(ocsp_ctx_p, config, target);
+       if (res != 0)
+               return res;
+       q = (*ocsp_ctx_p)->ocsp_query;
+
+       *ocsp_url = q->ocsp_url;
+       *request_blob = q->request_data;
+       *request_size = q->request_size;
+
+       return 0;
+}
+
+/*
+ * Public API for request blobs.
+ */
+
+int
+tls_ocsp_check_peer_request(struct tls **ocsp_ctx_p, struct tls *target,
+                           char **ocsp_url, void **request_blob, size_t 
*request_size)
+{
+       return tls_ocsp_create_request(ocsp_ctx_p, NULL, target,
+                       ocsp_url, request_blob, request_size);
+}
+
+int
+tls_ocsp_refresh_stapling_request(struct tls **ocsp_ctx_p,
+               struct tls_config *config,
+               char **ocsp_url, void **request_blob, size_t *request_size)
+{
+       return tls_ocsp_create_request(ocsp_ctx_p, config, NULL,
+                       ocsp_url, request_blob, request_size);
+}
+
+int
+tls_ocsp_process_response(struct tls *ctx, const void *response_blob, size_t 
size)
+{
+       int ret;
+       OCSP_RESPONSE *resp;
+       const unsigned char *raw = response_blob;
+
+       resp = d2i_OCSP_RESPONSE(NULL, &raw, size);
+       if (!resp) {
+               ctx->ocsp_result = "parse-failed";
+               tls_set_error(ctx, "parse failed");
+               return -1;
+       }
+       ret = tls_ocsp_process_response_parsed(ctx, ctx->config, resp);
+       OCSP_RESPONSE_free(resp);
+       return ret;
+}
+
diff --git lib/libtls/tls_server.c lib/libtls/tls_server.c
index 1d94c99..2d383d2 100644
--- lib/libtls/tls_server.c
+++ lib/libtls/tls_server.c
@@ -93,6 +93,11 @@ tls_configure_server(struct tls *ctx)
                SSL_CTX_set_options(ctx->ssl_ctx,
                    SSL_OP_CIPHER_SERVER_PREFERENCE);
 
+       if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, 
tls_ocsp_stapling_callback) != 1) {
+               tls_set_errorx(ctx, "ssl OCSP stapling setup failure");
+               goto err;
+       }
+
        /*
         * Set session ID context to a random value.  We don't support
         * persistent caching of sessions so it is OK to set a temporary

Reply via email to