diff options
author | Bob Beck <beck@cvs.openbsd.org> | 2016-11-02 15:18:43 +0000 |
---|---|---|
committer | Bob Beck <beck@cvs.openbsd.org> | 2016-11-02 15:18:43 +0000 |
commit | f8cf67d846573a981124a33cef8a510b7ccd50e8 (patch) | |
tree | 26965b83fc5c416e9409a1898b6883d20aa73274 /lib/libtls | |
parent | 7b5e710427f493441c7c1fbbd18418101fb6f779 (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/Makefile | 3 | ||||
-rw-r--r-- | lib/libtls/tls.c | 7 | ||||
-rw-r--r-- | lib/libtls/tls.h | 37 | ||||
-rw-r--r-- | lib/libtls/tls_client.c | 12 | ||||
-rw-r--r-- | lib/libtls/tls_init.3 | 121 | ||||
-rw-r--r-- | lib/libtls/tls_internal.h | 33 | ||||
-rw-r--r-- | lib/libtls/tls_ocsp.c | 399 |
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; +} |