summaryrefslogtreecommitdiff
path: root/lib/libtls
diff options
context:
space:
mode:
authorBob Beck <beck@cvs.openbsd.org>2016-11-02 15:18:43 +0000
committerBob Beck <beck@cvs.openbsd.org>2016-11-02 15:18:43 +0000
commitf8cf67d846573a981124a33cef8a510b7ccd50e8 (patch)
tree26965b83fc5c416e9409a1898b6883d20aa73274 /lib/libtls
parent7b5e710427f493441c7c1fbbd18418101fb6f779 (diff)
Add OCSP client side support to libtls.
- Provide access to certificate OCSP URL - Provide ability to check a raw OCSP reply against an established TLS ctx - Check and validate OCSP stapling info in the TLS handshake if a stapled OCSP response is provided.` Add example code to show OCSP URL and stapled info into netcat. ok jsing@
Diffstat (limited to 'lib/libtls')
-rw-r--r--lib/libtls/Makefile3
-rw-r--r--lib/libtls/tls.c7
-rw-r--r--lib/libtls/tls.h37
-rw-r--r--lib/libtls/tls_client.c12
-rw-r--r--lib/libtls/tls_init.3121
-rw-r--r--lib/libtls/tls_internal.h33
-rw-r--r--lib/libtls/tls_ocsp.c399
7 files changed, 604 insertions, 8 deletions
diff --git a/lib/libtls/Makefile b/lib/libtls/Makefile
index 245b1def6eb..f90c57d53fd 100644
--- a/lib/libtls/Makefile
+++ b/lib/libtls/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.25 2016/09/19 03:25:22 bcook Exp $
+# $OpenBSD: Makefile,v 1.26 2016/11/02 15:18:42 beck Exp $
CFLAGS+= -Wall -Werror -Wimplicit
CFLAGS+= -DLIBRESSL_INTERNAL
@@ -20,6 +20,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 a/lib/libtls/tls.c b/lib/libtls/tls.c
index 00564edb3c8..cccdb00531a 100644
--- a/lib/libtls/tls.c
+++ b/lib/libtls/tls.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls.c,v 1.49 2016/09/04 12:26:43 bcook Exp $ */
+/* $OpenBSD: tls.c,v 1.50 2016/11/02 15:18:42 beck Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
@@ -419,6 +419,9 @@ tls_reset(struct tls *ctx)
tls_conninfo_free(ctx->conninfo);
ctx->conninfo = NULL;
+ tls_ocsp_ctx_free(ctx->ocsp_ctx);
+ ctx->ocsp_ctx = NULL;
+
for (sni = ctx->sni_ctx; sni != NULL; sni = nsni) {
nsni = sni->next;
tls_sni_ctx_free(sni);
@@ -499,6 +502,8 @@ tls_handshake(struct tls *ctx)
ctx->ssl_peer_cert = SSL_get_peer_certificate(ctx->ssl_conn);
if (tls_conninfo_populate(ctx) == -1)
rv = -1;
+ if (ctx->ocsp_ctx == NULL)
+ ctx->ocsp_ctx = tls_ocsp_setup_from_peer(ctx);
}
out:
/* Prevent callers from performing incorrect error handling */
diff --git a/lib/libtls/tls.h b/lib/libtls/tls.h
index 6cc3d0d6f8b..3929cb848e4 100644
--- a/lib/libtls/tls.h
+++ b/lib/libtls/tls.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls.h,v 1.38 2016/09/13 13:40:58 tedu Exp $ */
+/* $OpenBSD: tls.h,v 1.39 2016/11/02 15:18:42 beck Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
@@ -41,6 +41,31 @@ extern "C" {
#define TLS_WANT_POLLIN -2
#define TLS_WANT_POLLOUT -3
+/* RFC 6960 Section 2.3 */
+#define TLS_OCSP_RESPONSE_SUCCESSFUL 0
+#define TLS_OCSP_RESPONSE_MALFORMED 1
+#define TLS_OCSP_RESPONSE_INTERNALERROR 2
+#define TLS_OCSP_RESPONSE_TRYLATER 3
+#define TLS_OCSP_RESPONSE_SIGREQUIRED 4
+#define TLS_OCSP_RESPONSE_UNAUTHORIZED 5
+
+/* RFC 6960 Section 2.2 */
+#define TLS_OCSP_CERT_GOOD 0
+#define TLS_OCSP_CERT_REVOKED 1
+#define TLS_OCSP_CERT_UNKNOWN 2
+
+/* RFC 5280 Section 5.3.1 */
+#define TLS_CRL_REASON_UNSPECIFIED 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_WITHDRAWN 9
+#define TLS_CRL_REASON_AA_COMPROMISE 10
+
struct tls;
struct tls_config;
@@ -138,6 +163,16 @@ const char *tls_conn_version(struct tls *_ctx);
uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password);
+int tls_ocsp_process_response(struct tls *_ctx, const unsigned char *_response, size_t _size);
+int tls_peer_ocsp_cert_status(struct tls *_ctx);
+int tls_peer_ocsp_crl_reason(struct tls *_ctx);
+time_t tls_peer_ocsp_next_update(struct tls *_ctx);
+int tls_peer_ocsp_response_status(struct tls *_ctx);
+const char *tls_peer_ocsp_result(struct tls *_ctx);
+time_t tls_peer_ocsp_revocation_time(struct tls *_ctx);
+time_t tls_peer_ocsp_this_update(struct tls *_ctx);
+const char *tls_peer_ocsp_url(struct tls *_ctx);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/libtls/tls_client.c b/lib/libtls/tls_client.c
index a1bceb7d973..84f4e91740c 100644
--- a/lib/libtls/tls_client.c
+++ b/lib/libtls/tls_client.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_client.c,v 1.36 2016/09/04 13:20:56 jsing Exp $ */
+/* $OpenBSD: tls_client.c,v 1.37 2016/11/02 15:18:42 beck Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
@@ -200,6 +200,11 @@ tls_connect_common(struct tls *ctx, const char *servername)
SSL_VERIFY_PEER) == -1))
goto err;
+ if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, tls_ocsp_verify_cb) != 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;
@@ -210,6 +215,11 @@ tls_connect_common(struct tls *ctx, const char *servername)
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
* permitted in "HostName".
diff --git a/lib/libtls/tls_init.3 b/lib/libtls/tls_init.3
index 6059c3f8a07..2f6ca3d8020 100644
--- a/lib/libtls/tls_init.3
+++ b/lib/libtls/tls_init.3
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tls_init.3,v 1.72 2016/09/13 13:40:58 tedu Exp $
+.\" $OpenBSD: tls_init.3,v 1.73 2016/11/02 15:18:42 beck Exp $
.\"
.\" Copyright (c) 2014 Ted Unangst <tedu@openbsd.org>
.\"
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: September 13 2016 $
+.Dd $Mdocdate: November 2 2016 $
.Dt TLS_INIT 3
.Os
.Sh NAME
@@ -50,6 +50,7 @@
.Nm tls_config_verify ,
.Nm tls_config_verify_client ,
.Nm tls_config_verify_client_optional ,
+.Nm tls_ocsp_process_response,
.Nm tls_peer_cert_provided ,
.Nm tls_peer_cert_contains_name ,
.Nm tls_peer_cert_issuer ,
@@ -57,6 +58,14 @@
.Nm tls_peer_cert_hash ,
.Nm tls_peer_cert_notbefore ,
.Nm tls_peer_cert_notafter ,
+.Nm tls_peer_ocsp_cert_status,
+.Nm tls_peer_ocsp_crl_reason,
+.Nm tls_peer_ocsp_next_update,
+.Nm tls_peer_ocsp_response_status,
+.Nm tls_peer_ocsp_result_msg,
+.Nm tls_peer_ocsp_revocation_time,
+.Nm tls_peer_ocsp_this_update,
+.Nm tls_peer_ocsp_url,
.Nm tls_conn_alpn_selected ,
.Nm tls_conn_cipher ,
.Nm tls_conn_servername ,
@@ -160,6 +169,24 @@
.Fn tls_peer_cert_notbefore "struct tls *ctx"
.Ft "time_t"
.Fn tls_peer_cert_notafter "struct tls *ctx"
+.Ft "int"
+.Fn tls_ocsp_process_response "struct tls *ctx" "const unsigned char *response" "size_t size"
+.Ft "int"
+.Fn tls_peer_ocsp_cert_status "struct tls *ctx"
+.Ft "int"
+.Fn tls_peer_ocsp_crl_reason "struct tls *ctx"
+.Ft time_t
+.Fn tls_peer_ocsp_next_update "struct tls *ctx"
+.Ft "int"
+.Fn tls_peer_ocsp_response_status "struct tls *ctx"
+.Ft "const char *"
+.Fn tls_peer_ocsp_result_msg "struct tls *ctx"
+.Ft "time_t"
+.Fn tls_peer_ocsp_revocation_time "struct tls *ctx"
+.Ft "time_t"
+.Fn tls_peer_ocsp_this_update" struct tls *ctx"
+.Ft "const char *"
+.Fn tls_peer_ocsp_url "struct tls *ctx"
.Ft "const char *"
.Fn tls_conn_alpn_selected "struct tls *ctx"
.Ft "const char *"
@@ -513,6 +540,53 @@ the peer certificate from
returns the time corresponding to the end of the validity period of
the peer certificate from
.Ar ctx .
+.Ed
+.It
+.Fn tls_ocsp_process_response
+processes a raw ocsp response in
+.Ar response
+of size
+.Ar size
+to check the revocation status of the peer certificate from
+.Ar ctx .
+A successful return code of 0 indicates that the certificate has not been revoked.
+.Ed
+.It
+.Fn tls_peer_ocsp_url
+returns the URL for OCSP validation of the peer certificate from
+.Ar ctx
+.El
+.Pp
+The following functions return informaiton about the peer certificate from
+.Ar ctx
+tha was obtained by validating a stapled OCSP response during the handshake, or
+via a previous call to
+.Xr tls_ocsp_process_response
+.Bl -bullet -offset four
+.It
+.Fn tls_peer_ocsp_cert_status
+returns the OCSP certificate status code as per RFC 6960 section 2.2
+.Ed
+.It
+.Fn tls_peer_ocsp_crl_reason
+returns the OCSP certificate revocation reason status code as per RFC 5280
+section 5.3.1
+.Ed
+.It
+.Fn tls_peer_ocsp_next_update
+returns the OCSP next update time
+.Ed
+.It
+.Fn tls_peer_ocsp_response_status
+returns the OCSP response status as per RFC 6960 section 2.3
+.Ed
+.It
+.Fn tls_peer_ocsp_revocation_time
+returns the OCSP revocation time
+.Ed
+.It
+.Fn tls_peer_ocsp_this_update
+returns the OCSP this update time
.El
.Pp
The following are TLS related utility functions:
@@ -616,16 +690,57 @@ The
and
.Fn tls_peer_cert_contains_name
functions return 1 if the check succeeds, and 0 if it does not.
+.Pp
Functions that return a
.Vt time_t
will return a time in epoch-seconds on success, and -1 on error.
+.Pp
Functions that return a
.Vt ssize_t
will return a size on success, and -1 on error.
+.Pp
+The
+.Fn tls_peer_ocsp_response_status
+function returns one of
+.Ar TLS_OCSP_RESPONSE_SUCCESSFUL ,
+.Ar TLS_OCSP_RESPONSE_MALFORMED ,
+.Ar TLS_OCSP_RESPONSE_INTERNALERROR ,
+.Ar TLS_OCSP_RESPONSE_TRYLATER ,
+.Ar TLS_OCSP_RESPONSE_SIGREQUIRED ,
+or
+.AR TLS_OCSP_RESPONSE_UNAUTHORIZED
+on success, and -1 on error.
+.Pp
+The
+.Fn tls_peer_ocsp_cert_status
+function returns one of
+.Ar TLS_OCSP_CERT_GOOD ,
+.Ar TLS_OCSP_CERT_REVOKED ,
+or
+.Ar TLS_OCSP_CERT_UNKNOWN
+on success, and -1 on error.
+.Pp
+The
+.Fn tls_peer_ocsp_crl_reason
+function returns one of
+.Ar TLS_CRL_REASON_UNSPECIFIED ,
+.Ar TLS_CRL_REASON_KEY_COMPROMISE ,
+.Ar TLS_CRL_REASON_CA_COMPROMISE ,
+.Ar TLS_CRL_REASON_AFFILIATION_CHANGED ,
+.Ar TLS_CRL_REASON_SUPERSEDED ,
+.Ar TLS_CRL_REASON_CESSATION_OF_OPERATION ,
+.Ar TLS_CRL_REASON_CERTIFICATE_HOLD ,
+.Ar TLS_CRL_REASON_REMOVE_FROM_CRL ,
+.Ar TLS_CRL_REASON_PRIVILEGE_WITHDRAWN ,
+or
+.Ar TLS_CRL_REASON_AA_COMPROMISE
+on success, and -1 on error.
+.Pp
All other functions that return
.Vt int
will return 0 on success and -1 on error.
-Functions that return a pointer will return NULL on error, which indicates an
+.Pp
+Functions that return a pointer will return NULL on error or an
out of memory condition.
.Pp
The
diff --git a/lib/libtls/tls_internal.h b/lib/libtls/tls_internal.h
index c272038055a..df35db37f2e 100644
--- a/lib/libtls/tls_internal.h
+++ b/lib/libtls/tls_internal.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls_internal.h,v 1.43 2016/09/04 12:26:43 bcook Exp $ */
+/* $OpenBSD: tls_internal.h,v 1.44 2016/11/02 15:18:42 beck Exp $ */
/*
* Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org>
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
@@ -92,6 +92,31 @@ struct tls_conninfo {
#define TLS_EOF_NO_CLOSE_NOTIFY (1 << 0)
#define TLS_HANDSHAKE_COMPLETE (1 << 1)
+struct tls_ocsp_result {
+ const char *result_msg;
+ int response_status;
+ int cert_status;
+ int crl_reason;
+ time_t this_update;
+ time_t next_update;
+ time_t revocation_time;
+};
+
+struct tls_ocsp_ctx {
+ /* responder location */
+ char *ocsp_url;
+
+ /* request blob */
+ uint8_t *request_data;
+ size_t request_size;
+
+ /* cert data, this struct does not own these */
+ X509 *main_cert;
+ STACK_OF(X509) *extra_certs;
+
+ struct tls_ocsp_result *ocsp_result;
+};
+
struct tls_sni_ctx {
struct tls_sni_ctx *next;
@@ -118,6 +143,8 @@ struct tls {
struct tls_conninfo *conninfo;
+ struct tls_ocsp_ctx *ocsp_ctx;
+
tls_read_cb read_cb;
tls_write_cb write_cb;
void *cb_arg;
@@ -172,6 +199,10 @@ int tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret,
int tls_conninfo_populate(struct tls *ctx);
void tls_conninfo_free(struct tls_conninfo *conninfo);
+int tls_ocsp_verify_cb(SSL *ssl, void *arg);
+void tls_ocsp_ctx_free(struct tls_ocsp_ctx *ctx);
+struct tls_ocsp_ctx *tls_ocsp_setup_from_peer(struct tls *ctx);
+
int asn1_time_parse(const char *, size_t, struct tm *, int);
#endif /* HEADER_TLS_INTERNAL_H */
diff --git a/lib/libtls/tls_ocsp.c b/lib/libtls/tls_ocsp.c
new file mode 100644
index 00000000000..113ab0dd3dc
--- /dev/null
+++ b/lib/libtls/tls_ocsp.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 2015 Marko Kreen <markokr@gmail.com>
+ * Copyright (c) 2016 Bob Beck <beck@openbsd.org>
+ *
+ * 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.
+ */
+
+static struct tls_ocsp_ctx *
+tls_ocsp_ctx_new(void)
+{
+ return (calloc(1, sizeof(struct tls_ocsp_ctx)));
+}
+
+void
+tls_ocsp_ctx_free(struct tls_ocsp_ctx *ocsp_ctx)
+{
+ if (ocsp_ctx == NULL)
+ return;
+
+ free(ocsp_ctx->ocsp_result);
+ ocsp_ctx->ocsp_result = NULL;
+ free(ocsp_ctx->ocsp_url);
+ ocsp_ctx->ocsp_url = NULL;
+ free(ocsp_ctx->request_data);
+ ocsp_ctx->request_data = NULL;
+ free(ocsp_ctx);
+}
+
+static int
+tls_ocsp_asn1_parse_time(struct tls *ctx, ASN1_GENERALIZEDTIME *gt, time_t *time)
+{
+ struct tm tm;
+
+ if (gt == NULL)
+ return -1;
+ /* RFC 6960 specifies that all times in OCSP must be GENERALIZEDTIME */
+ if (asn1_time_parse(gt->data, gt->length, &tm,
+ V_ASN1_GENERALIZEDTIME) == -1)
+ return -1;
+ if ((*time = timegm(&tm)) == -1)
+ return -1;
+ return 0;
+}
+
+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_result *info = NULL;
+
+ free(ctx->ocsp_ctx->ocsp_result);
+ ctx->ocsp_ctx->ocsp_result = NULL;
+
+ if ((info = calloc(1, sizeof (struct tls_ocsp_result))) == NULL) {
+ tls_set_error(ctx, "calloc");
+ return -1;
+ }
+ info->response_status = response_status;
+ info->cert_status = cert_status;
+ info->crl_reason = crl_reason;
+ if (info->response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ info->result_msg =
+ OCSP_response_status_str(info->response_status);
+ } else if (info->cert_status != V_OCSP_CERTSTATUS_REVOKED) {
+ info->result_msg = OCSP_cert_status_str(info->cert_status);
+ } else {
+ info->result_msg = OCSP_crl_reason_str(info->crl_reason);
+ }
+ info->revocation_time = info->this_update = info->next_update = -1;
+ if (revtime != NULL &&
+ tls_ocsp_asn1_parse_time(ctx, revtime, &info->revocation_time) != 0) {
+ tls_set_error(ctx,
+ "unable to parse revocation time in OCSP reply");
+ goto error;
+ }
+ if (thisupd != NULL &&
+ tls_ocsp_asn1_parse_time(ctx, thisupd, &info->this_update) != 0) {
+ tls_set_error(ctx,
+ "unable to parse this update time in OCSP reply");
+ goto error;
+ }
+ if (nextupd != NULL &&
+ tls_ocsp_asn1_parse_time(ctx, nextupd, &info->next_update) != 0) {
+ tls_set_error(ctx,
+ "unable to parse next update time in OCSP reply");
+ goto error;
+ }
+ ctx->ocsp_ctx->ocsp_result = info;
+ return 0;
+ error:
+ free(info);
+ return -1;
+}
+
+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;
+
+ if ((issuer_name = X509_get_issuer_name(main_cert)) == NULL)
+ return NULL;
+
+ if (extra_certs != NULL) {
+ issuer = X509_find_by_subject(extra_certs, issuer_name);
+ if (issuer != NULL)
+ return OCSP_cert_to_id(NULL, main_cert, issuer);
+ }
+
+ if ((store = SSL_CTX_get_cert_store(ssl_ctx)) == NULL)
+ return NULL;
+ if (X509_STORE_CTX_init(&storectx, store, main_cert, extra_certs) != 1)
+ return NULL;
+ if (X509_STORE_get_by_subject(&storectx, X509_LU_X509, issuer_name,
+ &tmpobj) == 1) {
+ cid = OCSP_cert_to_id(NULL, main_cert, tmpobj.data.x509);
+ X509_OBJECT_free_contents(&tmpobj);
+ }
+ X509_STORE_CTX_cleanup(&storectx);
+ return cid;
+}
+
+struct tls_ocsp_ctx *
+tls_ocsp_setup_from_peer(struct tls *ctx)
+{
+ struct tls_ocsp_ctx *ocsp_ctx = NULL;
+ STACK_OF(OPENSSL_STRING) *ocsp_urls = NULL;
+
+ if ((ocsp_ctx = tls_ocsp_ctx_new()) == NULL)
+ goto failed;
+
+ /* steal state from ctx struct */
+ ocsp_ctx->main_cert = SSL_get_peer_certificate(ctx->ssl_conn);
+ ocsp_ctx->extra_certs = SSL_get_peer_cert_chain(ctx->ssl_conn);
+ if (ocsp_ctx->main_cert == NULL) {
+ tls_set_errorx(ctx, "no peer certificate for OCSP");
+ goto failed;
+ }
+
+ ocsp_urls = X509_get1_ocsp(ocsp_ctx->main_cert);
+ if (ocsp_urls == NULL)
+ goto failed;
+ ocsp_ctx->ocsp_url = strdup(sk_OPENSSL_STRING_value(ocsp_urls, 0));
+ if (ocsp_ctx->ocsp_url == NULL) {
+ tls_set_errorx(ctx, "out of memory");
+ goto failed;
+ }
+
+ X509_email_free(ocsp_urls);
+ return ocsp_ctx;
+
+ failed:
+ tls_ocsp_ctx_free(ocsp_ctx);
+ X509_email_free(ocsp_urls);
+ return NULL;
+}
+
+static int
+tls_ocsp_verify_response(struct tls *ctx, OCSP_RESPONSE *resp)
+{
+ OCSP_BASICRESP *br = 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 ret = -1;
+ unsigned long flags;
+
+ if ((br = OCSP_response_get1_basic(resp)) == NULL) {
+ tls_set_errorx(ctx, "cannot load ocsp reply");
+ goto error;
+ }
+
+ /*
+ * Skip validation of 'extra_certs' as this should be done
+ * already as part of main handshake.
+ */
+ flags = OCSP_TRUSTOTHER;
+
+ /* now verify */
+ if (OCSP_basic_verify(br, ctx->ocsp_ctx->extra_certs,
+ SSL_CTX_get_cert_store(ctx->ssl_ctx), flags) != 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: response - %s",
+ OCSP_response_status_str(response_status));
+ goto error;
+ }
+
+ cid = tls_ocsp_get_certid(ctx->ocsp_ctx->main_cert,
+ ctx->ocsp_ctx->extra_certs, ctx->ssl_ctx);
+ if (cid == NULL) {
+ tls_set_errorx(ctx, "ocsp verify failed: no issuer cert");
+ goto error;
+ }
+
+ if (OCSP_resp_find_status(br, cid, &cert_status, &crl_reason,
+ &revtime, &thisupd, &nextupd) != 1) {
+ tls_set_errorx(ctx, "ocsp verify failed: no result for cert");
+ goto error;
+ }
+
+ if (OCSP_check_validity(thisupd, nextupd, JITTER_SEC,
+ MAXAGE_SEC) != 1) {
+ tls_set_errorx(ctx,
+ "ocsp verify failed: ocsp response not current");
+ goto error;
+ }
+
+ if (tls_ocsp_fill_info(ctx, response_status, cert_status,
+ crl_reason, revtime, thisupd, nextupd) != 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;
+}
+
+/* TLS handshake verification callback for stapled requests */
+int
+tls_ocsp_verify_cb(SSL *ssl, void *arg)
+{
+ const unsigned char *raw = NULL;
+ int size, res = -1;
+ struct tls *ctx;
+
+ if ((ctx = SSL_get_app_data(ssl)) == NULL)
+ return -1;
+
+ size = SSL_get_tlsext_status_ocsp_resp(ssl, &raw);
+ if (size <= 0)
+ return 1;
+
+ tls_ocsp_ctx_free(ctx->ocsp_ctx);
+ ctx->ocsp_ctx = tls_ocsp_setup_from_peer(ctx);
+ if (ctx->ocsp_ctx != NULL)
+ res = tls_ocsp_process_response(ctx, raw, size);
+
+ return (res == 0) ? 1 : 0;
+}
+
+/*
+ * Public API
+ */
+
+/* Retrieve OSCP URL from peer certificate, if present */
+const char *
+tls_peer_ocsp_url(struct tls *ctx)
+{
+ if (ctx->ocsp_ctx == NULL)
+ return NULL;
+ return ctx->ocsp_ctx->ocsp_url;
+}
+
+const char *
+tls_peer_ocsp_result(struct tls *ctx)
+{
+ if (ctx->ocsp_ctx == NULL)
+ return NULL;
+ if (ctx->ocsp_ctx->ocsp_result == NULL)
+ return NULL;
+ return ctx->ocsp_ctx->ocsp_result->result_msg;
+}
+
+int
+tls_peer_ocsp_response_status(struct tls *ctx)
+{
+ if (ctx->ocsp_ctx == NULL)
+ return -1;
+ if (ctx->ocsp_ctx->ocsp_result == NULL)
+ return -1;
+ return ctx->ocsp_ctx->ocsp_result->response_status;
+}
+
+int
+tls_peer_ocsp_cert_status(struct tls *ctx)
+{
+ if (ctx->ocsp_ctx == NULL)
+ return -1;
+ if (ctx->ocsp_ctx->ocsp_result == NULL)
+ return -1;
+ return ctx->ocsp_ctx->ocsp_result->cert_status;
+}
+
+int
+tls_peer_ocsp_crl_reason(struct tls *ctx)
+{
+ if (ctx->ocsp_ctx == NULL)
+ return -1;
+ if (ctx->ocsp_ctx->ocsp_result == NULL)
+ return -1;
+ return ctx->ocsp_ctx->ocsp_result->crl_reason;
+}
+
+time_t
+tls_peer_ocsp_this_update(struct tls *ctx)
+{
+ if (ctx->ocsp_ctx == NULL)
+ return -1;
+ if (ctx->ocsp_ctx->ocsp_result == NULL)
+ return -1;
+ return ctx->ocsp_ctx->ocsp_result->this_update;
+}
+
+time_t
+tls_peer_ocsp_next_update(struct tls *ctx)
+{
+ if (ctx->ocsp_ctx == NULL)
+ return -1;
+ if (ctx->ocsp_ctx->ocsp_result == NULL)
+ return -1;
+ return ctx->ocsp_ctx->ocsp_result->next_update;
+}
+
+time_t
+tls_peer_ocsp_revocation_time(struct tls *ctx)
+{
+ if (ctx->ocsp_ctx == NULL)
+ return -1;
+ if (ctx->ocsp_ctx->ocsp_result == NULL)
+ return -1;
+ return ctx->ocsp_ctx->ocsp_result->revocation_time;
+}
+
+/*
+ * Process a raw OCSP response from an OCSP server request.
+ * OCSP details can then be retrieved with tls_peer_ocsp_* functions.
+ * returns 0 if certificate ok, -1 otherwise.
+ */
+int
+tls_ocsp_process_response(struct tls *ctx, const unsigned char *response,
+ size_t size)
+{
+ int ret;
+ OCSP_RESPONSE *resp;
+
+ resp = d2i_OCSP_RESPONSE(NULL, &response, size);
+ if (resp == NULL) {
+ tls_ocsp_ctx_free(ctx->ocsp_ctx);
+ ctx->ocsp_ctx = NULL;
+ tls_set_error(ctx, "unable to parse OCSP response");
+ return -1;
+ }
+ ret = tls_ocsp_verify_response(ctx, resp);
+ OCSP_RESPONSE_free(resp);
+ return ret;
+}