From: Dr. Stephen Henson Date: Wed, 26 Sep 2007 21:56:59 +0000 (+0000) Subject: Support for certificate status TLS extension. X-Git-Tag: OpenSSL_0_9_8k^2~674 X-Git-Url: https://git.openssl.org/?p=openssl.git;a=commitdiff_plain;h=67c8e7f41486934b6809673b6d836d38eaf2383b;ds=sidebyside Support for certificate status TLS extension. --- diff --git a/CHANGES b/CHANGES index dddc1eab43..73cf7b354a 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,13 @@ Changes between 0.9.8f and 0.9.9 [xx XXX xxxx] + *) Implement certificate status request TLS extension defined in RFC3546. + A client can set the appropriate parameters and receive the encoded + OCSP response via a callback. A server can query the supplied parameters + and set the encoded OCSP response in the callback. Add simplified examples + to s_client and s_server. + [Steve Henson] + *) Implement Opaque PRF Input TLS extension as specified in draft-rescorla-tls-opaque-prf-input-00.txt. Since this is not an official specification yet and no extension type assignment by diff --git a/apps/apps.h b/apps/apps.h index c7e490a271..4b1127f5e7 100644 --- a/apps/apps.h +++ b/apps/apps.h @@ -122,6 +122,9 @@ #ifndef OPENSSL_NO_ENGINE #include #endif +#ifndef OPENSSL_NO_OCSP +#include +#endif #include int app_RAND_load_file(const char *file, BIO *bio_e, int dont_warn); @@ -247,6 +250,12 @@ X509_STORE *setup_verify(BIO *bp, char *CAfile, char *CApath); ENGINE *setup_engine(BIO *err, const char *engine, int debug); #endif +#ifndef OPENSSL_NO_OCSP +OCSP_RESPONSE *process_responder(BIO *err, OCSP_REQUEST *req, + char *host, char *path, char *port, int use_ssl, + int req_timeout); +#endif + int load_config(BIO *err, CONF *cnf); char *make_config_name(void); diff --git a/apps/ocsp.c b/apps/ocsp.c index 219dc2f359..df0339b743 100644 --- a/apps/ocsp.c +++ b/apps/ocsp.c @@ -120,7 +120,6 @@ int MAIN(int argc, char **argv) long nsec = MAX_VALIDITY_PERIOD, maxage = -1; char *CAfile = NULL, *CApath = NULL; X509_STORE *store = NULL; - SSL_CTX *ctx = NULL; STACK_OF(X509) *sign_other = NULL, *verify_other = NULL, *rother = NULL; char *sign_certfile = NULL, *verify_certfile = NULL, *rcertfile = NULL; unsigned long sign_flags = 0, verify_flags = 0, rflags = 0; @@ -723,48 +722,14 @@ int MAIN(int argc, char **argv) else if (host) { #ifndef OPENSSL_NO_SOCK - cbio = BIO_new_connect(host); + resp = process_responder(bio_err, req, host, path, + port, use_ssl, req_timeout); + if (!resp) + goto end; #else BIO_printf(bio_err, "Error creating connect BIO - sockets not supported.\n"); goto end; #endif - if (!cbio) - { - BIO_printf(bio_err, "Error creating connect BIO\n"); - goto end; - } - if (port) BIO_set_conn_port(cbio, port); - if (use_ssl == 1) - { - BIO *sbio; -#if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3) - ctx = SSL_CTX_new(SSLv23_client_method()); -#elif !defined(OPENSSL_NO_SSL3) - ctx = SSL_CTX_new(SSLv3_client_method()); -#elif !defined(OPENSSL_NO_SSL2) - ctx = SSL_CTX_new(SSLv2_client_method()); -#else - BIO_printf(bio_err, "SSL is disabled\n"); - goto end; -#endif - if (ctx == NULL) - { - BIO_printf(bio_err, "Error creating SSL context.\n"); - goto end; - } - SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); - sbio = BIO_new_ssl(ctx, 1); - cbio = BIO_push(sbio, cbio); - } - - resp = query_responder(bio_err, cbio, path, req, req_timeout); - BIO_free_all(cbio); - cbio = NULL; - if (!resp) - { - BIO_printf(bio_err, "Error querying OCSP responsder\n"); - goto end; - } } else if (respin) { @@ -913,7 +878,6 @@ end: OPENSSL_free(host); OPENSSL_free(port); OPENSSL_free(path); - SSL_CTX_free(ctx); } OPENSSL_EXIT(ret); @@ -1334,4 +1298,51 @@ static OCSP_RESPONSE *query_responder(BIO *err, BIO *cbio, char *path, return rsp; } +OCSP_RESPONSE *process_responder(BIO *err, OCSP_REQUEST *req, + char *host, char *path, char *port, int use_ssl, + int req_timeout) + { + BIO *cbio = NULL; + SSL_CTX *ctx = NULL; + OCSP_RESPONSE *resp = NULL; + cbio = BIO_new_connect(host); + if (!cbio) + { + BIO_printf(err, "Error creating connect BIO\n"); + goto end; + } + if (port) BIO_set_conn_port(cbio, port); + if (use_ssl == 1) + { + BIO *sbio; +#if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3) + ctx = SSL_CTX_new(SSLv23_client_method()); +#elif !defined(OPENSSL_NO_SSL3) + ctx = SSL_CTX_new(SSLv3_client_method()); +#elif !defined(OPENSSL_NO_SSL2) + ctx = SSL_CTX_new(SSLv2_client_method()); +#else + BIO_printf(err, "SSL is disabled\n"); + goto end; +#endif + if (ctx == NULL) + { + BIO_printf(err, "Error creating SSL context.\n"); + goto end; + } + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + sbio = BIO_new_ssl(ctx, 1); + cbio = BIO_push(sbio, cbio); + } + resp = query_responder(err, cbio, path, req, req_timeout); + if (!resp) + BIO_printf(bio_err, "Error querying OCSP responsder\n"); + end: + if (ctx) + SSL_CTX_free(ctx); + if (cbio) + BIO_free_all(cbio); + return resp; + } + #endif diff --git a/apps/s_client.c b/apps/s_client.c index 3474e6c6a7..3ccaca3330 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -161,6 +161,7 @@ typedef unsigned int u_int; #include #include #include +#include #include "s_apps.h" #include "timeouts.h" @@ -196,12 +197,14 @@ static int c_Pause=0; static int c_debug=0; #ifndef OPENSSL_NO_TLSEXT static int c_tlsextdebug=0; +static int c_status_req=0; #endif static int c_msg=0; static int c_showcerts=0; static void sc_usage(void); static void print_stuff(BIO *berr,SSL *con,int full); +static int ocsp_resp_cb(SSL *s, void *arg); static BIO *bio_c_out=NULL; static int c_quiet=0; static int c_ign_eof=0; @@ -329,6 +332,7 @@ static void sc_usage(void) #ifndef OPENSSL_NO_TLSEXT BIO_printf(bio_err," -servername host - Set TLS extension servername in ClientHello\n"); BIO_printf(bio_err," -tlsextdebug - hex dump of all TLS extensions received\n"); + BIO_printf(bio_err," -status - request certificate status from server\n"); BIO_printf(bio_err," -no_ticket - disable use of RFC4507bis session tickets\n"); #endif } @@ -528,6 +532,8 @@ int MAIN(int argc, char **argv) #ifndef OPENSSL_NO_TLSEXT else if (strcmp(*argv,"-tlsextdebug") == 0) c_tlsextdebug=1; + else if (strcmp(*argv,"-status") == 0) + c_status_req=1; #endif #ifdef WATT32 else if (strcmp(*argv,"-wdebug") == 0) @@ -954,6 +960,23 @@ re_start: SSL_set_tlsext_debug_callback(con, tlsext_cb); SSL_set_tlsext_debug_arg(con, bio_c_out); } + if (c_status_req) + { + SSL_set_tlsext_status_type(con, TLSEXT_STATUSTYPE_ocsp); + SSL_CTX_set_tlsext_status_cb(ctx, ocsp_resp_cb); + SSL_CTX_set_tlsext_status_arg(ctx, bio_c_out); +#if 0 +{ +STACK_OF(OCSP_RESPID) *ids = sk_OCSP_RESPID_new_null(); +OCSP_RESPID *id = OCSP_RESPID_new(); +id->value.byKey = ASN1_OCTET_STRING_new(); +id->type = V_OCSP_RESPID_KEY; +ASN1_STRING_set(id->value.byKey, "Hello World", -1); +sk_OCSP_RESPID_push(ids, id); +SSL_set_tlsext_status_ids(con, ids); +} +#endif + } #endif SSL_set_bio(con,sbio,sbio); @@ -1592,3 +1615,28 @@ static void print_stuff(BIO *bio, SSL *s, int full) (void)BIO_flush(bio); } +static int ocsp_resp_cb(SSL *s, void *arg) + { + const unsigned char *p; + int len; + OCSP_RESPONSE *rsp; + len = SSL_get_tlsext_status_ocsp_resp(s, &p); + BIO_puts(arg, "OCSP response: "); + if (!p) + { + BIO_puts(arg, "no response sent\n"); + return 1; + } + rsp = d2i_OCSP_RESPONSE(NULL, &p, len); + if (!rsp) + { + BIO_puts(arg, "response parse error\n"); + BIO_dump_indent(arg, (char *)p, len, 4); + return 0; + } + BIO_puts(arg, "\n======================================\n"); + OCSP_RESPONSE_print(arg, rsp, 0); + BIO_puts(arg, "======================================\n"); + OCSP_RESPONSE_free(rsp); + return 1; + } diff --git a/apps/s_server.c b/apps/s_server.c index 3b99a620da..b3ec6091bc 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -179,6 +179,7 @@ typedef unsigned int u_int; #include #include #include +#include #ifndef OPENSSL_NO_DH #include #endif @@ -283,6 +284,8 @@ static BIO *bio_s_out=NULL; static int s_debug=0; #ifndef OPENSSL_NO_TLSEXT static int s_tlsextdebug=0; +static int s_tlsextstatus=0; +static int cert_status_cb(SSL *s, void *arg); #endif static int s_msg=0; static int s_quiet=0; @@ -664,6 +667,152 @@ static int MS_CALLBACK ssl_servername_cb(SSL *s, int *ad, void *arg) } return SSL_TLSEXT_ERR_OK; } + +/* Structure passed to cert status callback */ + +typedef struct tlsextstatusctx_st { + /* Default responder to use */ + char *host, *path, *port; + int use_ssl; + int timeout; + BIO *err; + int verbose; +} tlsextstatusctx; + +static tlsextstatusctx tlscstatp = {NULL, NULL, NULL, 0, -1, NULL, 0}; + +/* Certificate Status callback. This is called when a client includes a + * certificate status request extension. + * + * This is a simplified version. It examines certificates each time and + * makes one OCSP responder query for each request. + * + * A full version would store details such as the OCSP certificate IDs and + * minimise the number of OCSP responses by caching them until they were + * considered "expired". + */ + +static int cert_status_cb(SSL *s, void *arg) + { + tlsextstatusctx *srctx = arg; + BIO *err = srctx->err; + char *host, *port, *path; + int use_ssl; + unsigned char *rspder = NULL; + int rspderlen; + STACK *aia = NULL; + X509 *x = NULL; + X509_STORE_CTX inctx; + X509_OBJECT obj; + OCSP_REQUEST *req = NULL; + OCSP_RESPONSE *resp = NULL; + OCSP_CERTID *id = NULL; + STACK_OF(X509_EXTENSION) *exts; + int ret = SSL_TLSEXT_ERR_NOACK; + int i; +#if 0 +STACK_OF(OCSP_RESPID) *ids; +SSL_get_tlsext_status_ids(s, &ids); +BIO_printf(err, "cert_status: received %d ids\n", sk_OCSP_RESPID_num(ids)); +#endif + if (srctx->verbose) + BIO_puts(err, "cert_status: callback called\n"); + /* Build up OCSP query from server certificate */ + x = SSL_get_certificate(s); + aia = X509_get1_ocsp(x); + if (aia) + { + if (!OCSP_parse_url(sk_value(aia, 0), + &host, &port, &path, &use_ssl)) + { + BIO_puts(err, "cert_status: can't parse AIA URL\n"); + goto err; + } + if (srctx->verbose) + BIO_printf(err, "cert_status: AIA URL: %s\n", + sk_value(aia, 0)); + } + else + { + if (!srctx->host) + { + BIO_puts(srctx->err, "cert_status: no AIA and no default responder URL\n"); + goto done; + } + host = srctx->host; + path = srctx->path; + port = srctx->port; + use_ssl = srctx->use_ssl; + } + + if (!X509_STORE_CTX_init(&inctx, + SSL_CTX_get_cert_store(SSL_get_SSL_CTX(s)), + NULL, NULL)) + goto err; + if (X509_STORE_get_by_subject(&inctx,X509_LU_X509, + X509_get_issuer_name(x),&obj) <= 0) + { + BIO_puts(err, "cert_status: Can't retrieve issuer certificate.\n"); + X509_STORE_CTX_cleanup(&inctx); + goto done; + } + req = OCSP_REQUEST_new(); + if (!req) + goto err; + id = OCSP_cert_to_id(NULL, x, obj.data.x509); + X509_free(obj.data.x509); + X509_STORE_CTX_cleanup(&inctx); + if (!id) + goto err; + if (!OCSP_request_add0_id(req, id)) + goto err; + id = NULL; + /* Add any extensions to the request */ + SSL_get_tlsext_status_exts(s, &exts); + for (i = 0; i < sk_X509_EXTENSION_num(exts); i++) + { + X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i); + if (!OCSP_REQUEST_add_ext(req, ext, -1)) + goto err; + } + resp = process_responder(err, req, host, path, port, use_ssl, + srctx->timeout); + if (!resp) + { + BIO_puts(err, "cert_status: error querying responder\n"); + goto done; + } + rspderlen = i2d_OCSP_RESPONSE(resp, &rspder); + if (rspderlen <= 0) + goto err; + SSL_set_tlsext_status_ocsp_resp(s, rspder, rspderlen); + if (srctx->verbose) + { + BIO_puts(err, "cert_status: ocsp response sent:\n"); + OCSP_RESPONSE_print(err, resp, 2); + } + ret = SSL_TLSEXT_ERR_OK; + done: + if (ret != SSL_TLSEXT_ERR_OK) + ERR_print_errors(err); + if (aia) + { + OPENSSL_free(host); + OPENSSL_free(path); + OPENSSL_free(port); + X509_email_free(aia); + } + if (id) + OCSP_CERTID_free(id); + if (req) + OCSP_REQUEST_free(req); + if (resp) + OCSP_RESPONSE_free(resp); + return ret; + err: + ret = SSL_TLSEXT_ERR_ALERT_FATAL; + goto done; + } #endif int MAIN(int, char **); @@ -877,6 +1026,33 @@ int MAIN(int argc, char *argv[]) #ifndef OPENSSL_NO_TLSEXT else if (strcmp(*argv,"-tlsextdebug") == 0) s_tlsextdebug=1; + else if (strcmp(*argv,"-status") == 0) + s_tlsextstatus=1; + else if (strcmp(*argv,"-status_verbose") == 0) + { + s_tlsextstatus=1; + tlscstatp.verbose = 1; + } + else if (!strcmp(*argv, "-status_timeout")) + { + s_tlsextstatus=1; + if (--argc < 1) goto bad; + tlscstatp.timeout = atoi(*(++argv)); + } + else if (!strcmp(*argv, "-status_url")) + { + s_tlsextstatus=1; + if (--argc < 1) goto bad; + if (!OCSP_parse_url(*(++argv), + &tlscstatp.host, + &tlscstatp.port, + &tlscstatp.path, + &tlscstatp.use_ssl)) + { + BIO_printf(bio_err, "Error parsing URL\n"); + goto bad; + } + } #endif else if (strcmp(*argv,"-msg") == 0) { s_msg=1; } @@ -1560,6 +1736,12 @@ static int sv_body(char *hostname, int s, unsigned char *context) SSL_set_tlsext_debug_callback(con, tlsext_cb); SSL_set_tlsext_debug_arg(con, bio_s_out); } + if (s_tlsextstatus); + { + SSL_CTX_set_tlsext_status_cb(ctx, cert_status_cb); + tlscstatp.err = bio_err; + SSL_CTX_set_tlsext_status_arg(ctx, &tlscstatp); + } #endif #ifndef OPENSSL_NO_KRB5 if ((con->kssl_ctx = kssl_ctx_new()) != NULL) diff --git a/apps/x509.c b/apps/x509.c index dfe41a6f1f..1fa93aaeba 100644 --- a/apps/x509.c +++ b/apps/x509.c @@ -114,6 +114,7 @@ static const char *x509_usage[]={ " -alias - output certificate alias\n", " -noout - no certificate output\n", " -ocspid - print OCSP hash values for the subject name and public key\n", +" -ocspurl - print OCSP Responder URL(s)\n", " -trustout - output a \"trusted\" certificate\n", " -clrtrust - clear all trusted purposes\n", " -clrreject - clear all rejected purposes\n", @@ -179,6 +180,7 @@ int MAIN(int argc, char **argv) int next_serial=0; int subject_hash=0,issuer_hash=0,ocspid=0; int noout=0,sign_flag=0,CA_flag=0,CA_createserial=0,email=0; + int ocsp_uri=0; int trustout=0,clrtrust=0,clrreject=0,aliasout=0,clrext=0; int C=0; int x509req=0,days=DEF_DAYS,modulus=0,pubkey=0; @@ -378,6 +380,8 @@ int MAIN(int argc, char **argv) C= ++num; else if (strcmp(*argv,"-email") == 0) email= ++num; + else if (strcmp(*argv,"-ocsp_uri") == 0) + ocsp_uri= ++num; else if (strcmp(*argv,"-serial") == 0) serial= ++num; else if (strcmp(*argv,"-next_serial") == 0) @@ -731,11 +735,14 @@ bad: ASN1_INTEGER_free(ser); BIO_puts(out, "\n"); } - else if (email == i) + else if ((email == i) || (ocsp_uri == i)) { int j; STACK *emlst; - emlst = X509_get1_email(x); + if (email == i) + emlst = X509_get1_email(x); + else + emlst = X509_get1_ocsp(x); for (j = 0; j < sk_num(emlst); j++) BIO_printf(STDout, "%s\n", sk_value(emlst, j)); X509_email_free(emlst); diff --git a/crypto/asn1/x_exten.c b/crypto/asn1/x_exten.c index 702421b6c8..1732e66712 100644 --- a/crypto/asn1/x_exten.c +++ b/crypto/asn1/x_exten.c @@ -67,5 +67,10 @@ ASN1_SEQUENCE(X509_EXTENSION) = { ASN1_SIMPLE(X509_EXTENSION, value, ASN1_OCTET_STRING) } ASN1_SEQUENCE_END(X509_EXTENSION) +ASN1_ITEM_TEMPLATE(X509_EXTENSIONS) = + ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, Extension, X509_EXTENSION) +ASN1_ITEM_TEMPLATE_END(X509_EXTENSIONS) + IMPLEMENT_ASN1_FUNCTIONS(X509_EXTENSION) +IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(X509_EXTENSIONS, X509_EXTENSIONS, X509_EXTENSIONS) IMPLEMENT_ASN1_DUP_FUNCTION(X509_EXTENSION) diff --git a/crypto/ocsp/ocsp.h b/crypto/ocsp/ocsp.h index 34bb62c277..bbfc5055df 100644 --- a/crypto/ocsp/ocsp.h +++ b/crypto/ocsp/ocsp.h @@ -187,11 +187,11 @@ typedef struct ocsp_resp_bytes_st * responseStatus OCSPResponseStatus, * responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } */ -typedef struct ocsp_response_st +struct ocsp_response_st { ASN1_ENUMERATED *responseStatus; OCSP_RESPBYTES *responseBytes; - } OCSP_RESPONSE; + }; /* ResponderID ::= CHOICE { * byName [1] Name, @@ -199,14 +199,18 @@ typedef struct ocsp_response_st */ #define V_OCSP_RESPID_NAME 0 #define V_OCSP_RESPID_KEY 1 -typedef struct ocsp_responder_id_st +struct ocsp_responder_id_st { int type; union { X509_NAME* byName; ASN1_OCTET_STRING *byKey; } value; - } OCSP_RESPID; + }; + +DECLARE_STACK_OF(OCSP_RESPID) +DECLARE_ASN1_FUNCTIONS(OCSP_RESPID) + /* KeyHash ::= OCTET STRING --SHA-1 hash of responder's public key * --(excluding the tag and length fields) */ diff --git a/crypto/ossl_typ.h b/crypto/ossl_typ.h index b132405098..2a66d484b9 100644 --- a/crypto/ossl_typ.h +++ b/crypto/ossl_typ.h @@ -188,5 +188,7 @@ typedef int CRYPTO_EX_dup(CRYPTO_EX_DATA *to, CRYPTO_EX_DATA *from, void *from_d int idx, long argl, void *argp); typedef struct ocsp_req_ctx_st OCSP_REQ_CTX; +typedef struct ocsp_response_st OCSP_RESPONSE; +typedef struct ocsp_responder_id_st OCSP_RESPID; #endif /* def HEADER_OPENSSL_TYPES_H */ diff --git a/crypto/stack/safestack.h b/crypto/stack/safestack.h index 250929bf7d..f80353598f 100644 --- a/crypto/stack/safestack.h +++ b/crypto/stack/safestack.h @@ -1074,6 +1074,28 @@ STACK_OF(type) \ #define sk_OCSP_ONEREQ_sort(st) SKM_sk_sort(OCSP_ONEREQ, (st)) #define sk_OCSP_ONEREQ_is_sorted(st) SKM_sk_is_sorted(OCSP_ONEREQ, (st)) +#define sk_OCSP_RESPID_new(st) SKM_sk_new(OCSP_RESPID, (st)) +#define sk_OCSP_RESPID_new_null() SKM_sk_new_null(OCSP_RESPID) +#define sk_OCSP_RESPID_free(st) SKM_sk_free(OCSP_RESPID, (st)) +#define sk_OCSP_RESPID_num(st) SKM_sk_num(OCSP_RESPID, (st)) +#define sk_OCSP_RESPID_value(st, i) SKM_sk_value(OCSP_RESPID, (st), (i)) +#define sk_OCSP_RESPID_set(st, i, val) SKM_sk_set(OCSP_RESPID, (st), (i), (val)) +#define sk_OCSP_RESPID_zero(st) SKM_sk_zero(OCSP_RESPID, (st)) +#define sk_OCSP_RESPID_push(st, val) SKM_sk_push(OCSP_RESPID, (st), (val)) +#define sk_OCSP_RESPID_unshift(st, val) SKM_sk_unshift(OCSP_RESPID, (st), (val)) +#define sk_OCSP_RESPID_find(st, val) SKM_sk_find(OCSP_RESPID, (st), (val)) +#define sk_OCSP_RESPID_find_ex(st, val) SKM_sk_find_ex(OCSP_RESPID, (st), (val)) +#define sk_OCSP_RESPID_delete(st, i) SKM_sk_delete(OCSP_RESPID, (st), (i)) +#define sk_OCSP_RESPID_delete_ptr(st, ptr) SKM_sk_delete_ptr(OCSP_RESPID, (st), (ptr)) +#define sk_OCSP_RESPID_insert(st, val, i) SKM_sk_insert(OCSP_RESPID, (st), (val), (i)) +#define sk_OCSP_RESPID_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(OCSP_RESPID, (st), (cmp)) +#define sk_OCSP_RESPID_dup(st) SKM_sk_dup(OCSP_RESPID, st) +#define sk_OCSP_RESPID_pop_free(st, free_func) SKM_sk_pop_free(OCSP_RESPID, (st), (free_func)) +#define sk_OCSP_RESPID_shift(st) SKM_sk_shift(OCSP_RESPID, (st)) +#define sk_OCSP_RESPID_pop(st) SKM_sk_pop(OCSP_RESPID, (st)) +#define sk_OCSP_RESPID_sort(st) SKM_sk_sort(OCSP_RESPID, (st)) +#define sk_OCSP_RESPID_is_sorted(st) SKM_sk_is_sorted(OCSP_RESPID, (st)) + #define sk_OCSP_SINGLERESP_new(st) SKM_sk_new(OCSP_SINGLERESP, (st)) #define sk_OCSP_SINGLERESP_new_null() SKM_sk_new_null(OCSP_SINGLERESP) #define sk_OCSP_SINGLERESP_free(st) SKM_sk_free(OCSP_SINGLERESP, (st)) diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h index 914fb04b1d..4f40712448 100644 --- a/crypto/x509/x509.h +++ b/crypto/x509/x509.h @@ -205,6 +205,8 @@ typedef struct X509_extension_st ASN1_OCTET_STRING *value; } X509_EXTENSION; +typedef STACK_OF(X509_EXTENSION) X509_EXTENSIONS; + DECLARE_STACK_OF(X509_EXTENSION) DECLARE_ASN1_SET_OF(X509_EXTENSION) @@ -801,6 +803,7 @@ DECLARE_ASN1_FUNCTIONS(X509_ATTRIBUTE) X509_ATTRIBUTE *X509_ATTRIBUTE_create(int nid, int atrtype, void *value); DECLARE_ASN1_FUNCTIONS(X509_EXTENSION) +DECLARE_ASN1_ENCODE_FUNCTIONS(X509_EXTENSIONS, X509_EXTENSIONS, X509_EXTENSIONS) DECLARE_ASN1_FUNCTIONS(X509_NAME_ENTRY) diff --git a/crypto/x509/x509_req.c b/crypto/x509/x509_req.c index a2d78a8a12..48183dc00c 100644 --- a/crypto/x509/x509_req.c +++ b/crypto/x509/x509_req.c @@ -67,10 +67,6 @@ #include #include -ASN1_ITEM_TEMPLATE(X509_EXTENSIONS) = - ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, Extension, X509_EXTENSION) -ASN1_ITEM_TEMPLATE_END(X509_EXTENSIONS) - X509_REQ *X509_to_X509_REQ(X509 *x, EVP_PKEY *pkey, const EVP_MD *md) { X509_REQ *ret; diff --git a/crypto/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c index f1b5c94c2a..6aa54aedfa 100644 --- a/crypto/x509v3/v3_utl.c +++ b/crypto/x509v3/v3_utl.c @@ -473,6 +473,30 @@ STACK *X509_get1_email(X509 *x) return ret; } +STACK *X509_get1_ocsp(X509 *x) +{ + AUTHORITY_INFO_ACCESS *info; + STACK *ret = NULL; + int i; + info = X509_get_ext_d2i(x, NID_info_access, NULL, NULL); + if (!info) + return NULL; + for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++) + { + ACCESS_DESCRIPTION *ad = sk_ACCESS_DESCRIPTION_value(info, i); + if (OBJ_obj2nid(ad->method) == NID_ad_OCSP) + { + if (ad->location->type == GEN_URI) + { + if (!append_ia5(&ret, ad->location->d.uniformResourceIdentifier)) + break; + } + } + } + AUTHORITY_INFO_ACCESS_free(info); + return ret; +} + STACK *X509_REQ_get1_email(X509_REQ *x) { GENERAL_NAMES *gens; diff --git a/crypto/x509v3/x509v3.h b/crypto/x509v3/x509v3.h index cbadc4c185..be0c97a404 100644 --- a/crypto/x509v3/x509v3.h +++ b/crypto/x509v3/x509v3.h @@ -649,6 +649,7 @@ int X509_PURPOSE_get_id(X509_PURPOSE *); STACK *X509_get1_email(X509 *x); STACK *X509_REQ_get1_email(X509_REQ *x); void X509_email_free(STACK *sk); +STACK *X509_get1_ocsp(X509 *x); ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc); ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc); diff --git a/ssl/s23_clnt.c b/ssl/s23_clnt.c index c500a932a7..78c39d9af5 100644 --- a/ssl/s23_clnt.c +++ b/ssl/s23_clnt.c @@ -283,6 +283,8 @@ static int ssl23_client_hello(SSL *s) if (s->tlsext_hostname != NULL) ssl2_compat = 0; + if (s->tlsext_status_type != -1) + ssl2_compat = 0; #ifdef TLSEXT_TYPE_opaque_prf_input if (s->ctx->tlsext_opaque_prf_input_callback != 0 || s->tlsext_opaque_prf_input != NULL) diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index f8f43eb938..b7d8d4213b 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c @@ -307,10 +307,23 @@ int ssl3_connect(SSL *s) { ret=ssl3_get_server_certificate(s); if (ret <= 0) goto end; +#ifndef OPENSSL_NO_TLSEXT + if (s->tlsext_status_expected) + s->state=SSL3_ST_CR_CERT_STATUS_A; + else + s->state=SSL3_ST_CR_KEY_EXCH_A; + } + else + { + skip = 1; + s->state=SSL3_ST_CR_KEY_EXCH_A; } +#else else skip=1; + s->state=SSL3_ST_CR_KEY_EXCH_A; +#endif s->init_num=0; break; @@ -473,6 +486,14 @@ int ssl3_connect(SSL *s) s->state=SSL3_ST_CR_FINISHED_A; s->init_num=0; break; + + case SSL3_ST_CR_CERT_STATUS_A: + case SSL3_ST_CR_CERT_STATUS_B: + ret=ssl3_get_cert_status(s); + if (ret <= 0) goto end; + s->state=SSL3_ST_CR_KEY_EXCH_A; + s->init_num=0; + break; #endif case SSL3_ST_CR_FINISHED_A: @@ -1795,6 +1816,75 @@ f_err: err: return(-1); } + +int ssl3_get_cert_status(SSL *s) + { + int ok, al; + unsigned long resplen; + long n; + const unsigned char *p; + + n=s->method->ssl_get_message(s, + SSL3_ST_CR_CERT_STATUS_A, + SSL3_ST_CR_CERT_STATUS_B, + SSL3_MT_CERTIFICATE_STATUS, + 16384, + &ok); + + if (!ok) return((int)n); + if (n < 4) + { + /* need at least status type + length */ + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_SSL3_GET_CERT_STATUS,SSL_R_LENGTH_MISMATCH); + goto f_err; + } + p = (unsigned char *)s->init_msg; + if (*p++ != TLSEXT_STATUSTYPE_ocsp) + { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_SSL3_GET_CERT_STATUS,SSL_R_UNSUPPORTED_STATUS_TYPE); + goto f_err; + } + n2l3(p, resplen); + if (resplen + 4 != n) + { + al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_SSL3_GET_CERT_STATUS,SSL_R_LENGTH_MISMATCH); + goto f_err; + } + if (s->tlsext_ocsp_resp) + OPENSSL_free(s->tlsext_ocsp_resp); + s->tlsext_ocsp_resp = BUF_memdup(p, resplen); + if (!s->tlsext_ocsp_resp) + { + al = SSL_AD_INTERNAL_ERROR; + SSLerr(SSL_F_SSL3_GET_CERT_STATUS,ERR_R_MALLOC_FAILURE); + goto f_err; + } + s->tlsext_ocsp_resplen = resplen; + if (s->ctx->tlsext_status_cb) + { + int ret; + ret = s->ctx->tlsext_status_cb(s, s->ctx->tlsext_status_arg); + if (ret == 0) + { + al = SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE; + SSLerr(SSL_F_SSL3_GET_CERT_STATUS,SSL_R_INVALID_STATUS_RESPONSE); + goto f_err; + } + if (ret < 0) + { + al = SSL_AD_INTERNAL_ERROR; + SSLerr(SSL_F_SSL3_GET_CERT_STATUS,ERR_R_MALLOC_FAILURE); + goto f_err; + } + } + return 1; +f_err: + ssl3_send_alert(s,SSL3_AL_FATAL,al); + return(-1); + } #endif int ssl3_get_server_done(SSL *s) diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 548eeef6f3..9910a132e8 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -2383,6 +2383,43 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg) break; #endif + case SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE: + s->tlsext_status_type=larg; + ret = 1; + break; + + case SSL_CTRL_GET_TLSEXT_STATUS_REQ_EXTS: + *(STACK_OF(X509_EXTENSION) **)parg = s->tlsext_ocsp_exts; + ret = 1; + break; + + case SSL_CTRL_SET_TLSEXT_STATUS_REQ_EXTS: + s->tlsext_ocsp_exts = parg; + ret = 1; + break; + + case SSL_CTRL_GET_TLSEXT_STATUS_REQ_IDS: + *(STACK_OF(OCSP_RESPID) **)parg = s->tlsext_ocsp_ids; + ret = 1; + break; + + case SSL_CTRL_SET_TLSEXT_STATUS_REQ_IDS: + s->tlsext_ocsp_ids = parg; + ret = 1; + break; + + case SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP: + *(unsigned char **)parg = s->tlsext_ocsp_resp; + return s->tlsext_ocsp_resplen; + + case SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP: + if (s->tlsext_ocsp_resp) + OPENSSL_free(s->tlsext_ocsp_resp); + s->tlsext_ocsp_resp = parg; + s->tlsext_ocsp_resplen = larg; + ret = 1; + break; + #endif /* !OPENSSL_NO_TLSEXT */ default: break; @@ -2610,6 +2647,11 @@ long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) return 1; #endif + case SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB_ARG: + ctx->tlsext_status_arg=parg; + return 1; + break; + #endif /* !OPENSSL_NO_TLSEXT */ /* A Thawte special :-) */ @@ -2668,6 +2710,10 @@ long ssl3_ctx_callback_ctrl(SSL_CTX *ctx, int cmd, void (*fp)(void)) break; #endif + case SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB: + ctx->tlsext_status_cb=(int (*)(SSL *,void *))fp; + break; + #endif default: return(0); diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c index 697ab725bd..cc01cb109f 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -332,10 +332,23 @@ int ssl3_accept(SSL *s) { ret=ssl3_send_server_certificate(s); if (ret <= 0) goto end; +#ifndef OPENSSL_NO_TLSEXT + if (s->tlsext_status_expected) + s->state=SSL3_ST_SW_CERT_STATUS_A; + else + s->state=SSL3_ST_SW_KEY_EXCH_A; } + else + { + skip = 1; + s->state=SSL3_ST_SW_KEY_EXCH_A; + } +#else else skip=1; + s->state=SSL3_ST_SW_KEY_EXCH_A; +#endif s->init_num=0; break; @@ -551,6 +564,14 @@ int ssl3_accept(SSL *s) s->init_num=0; break; + case SSL3_ST_SW_CERT_STATUS_A: + case SSL3_ST_SW_CERT_STATUS_B: + ret=ssl3_send_cert_status(s); + if (ret <= 0) goto end; + s->state=SSL3_ST_SW_KEY_EXCH_A; + s->init_num=0; + break; + #endif case SSL3_ST_SW_CHANGE_A: @@ -2823,4 +2844,39 @@ int ssl3_send_newsession_ticket(SSL *s) /* SSL3_ST_SW_SESSION_TICKET_B */ return(ssl3_do_write(s,SSL3_RT_HANDSHAKE)); } + +int ssl3_send_cert_status(SSL *s) + { + if (s->state == SSL3_ST_SW_CERT_STATUS_A) + { + unsigned char *p; + /* Grow buffer if need be: the length calculation is as + * follows 1 (message type) + 3 (message length) + + * 1 (ocsp response type) + 3 (ocsp response length) + * + (ocsp response) + */ + if (!BUF_MEM_grow(s->init_buf, 8 + s->tlsext_ocsp_resplen)) + return -1; + + p=(unsigned char *)s->init_buf->data; + + /* do the header */ + *(p++)=SSL3_MT_CERTIFICATE_STATUS; + /* message length */ + l2n3(s->tlsext_ocsp_resplen + 4, p); + /* status type */ + *(p++)= s->tlsext_status_type; + /* length of OCSP response */ + l2n3(s->tlsext_ocsp_resplen, p); + /* actual response */ + memcpy(p, s->tlsext_ocsp_resp, s->tlsext_ocsp_resplen); + /* number of bytes to write */ + s->init_num = 8 + s->tlsext_ocsp_resplen; + s->state=SSL3_ST_SW_CERT_STATUS_B; + s->init_off = 0; + } + + /* SSL3_ST_SW_CERT_STATUS_B */ + return(ssl3_do_write(s,SSL3_RT_HANDSHAKE)); + } #endif diff --git a/ssl/ssl.h b/ssl/ssl.h index ec42280a9c..32f572ce46 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -800,6 +800,11 @@ struct ssl_ctx_st unsigned char tlsext_tick_hmac_key[16]; unsigned char tlsext_tick_aes_key[16]; + /* certificate status request info */ + /* Callback for status request */ + int (*tlsext_status_cb)(SSL *ssl, void *arg); + void *tlsext_status_arg; + /* draft-rescorla-tls-opaque-prf-input-00.txt information */ int (*tlsext_opaque_prf_input_callback)(SSL *, void *peerinput, size_t len, void *arg); void *tlsext_opaque_prf_input_callback_arg; @@ -1083,6 +1088,18 @@ struct ssl_st 1 : prepare 2, allow last ack just after in server callback. 2 : don't call servername callback, no ack in server hello */ + /* certificate status request info */ + /* Status type or -1 if no status type */ + int tlsext_status_type; + /* Expect OCSP CertificateStatus message */ + int tlsext_status_expected; + /* OCSP status request only */ + STACK_OF(OCSP_RESPID) *tlsext_ocsp_ids; + X509_EXTENSIONS *tlsext_ocsp_exts; + /* OCSP response received or to be sent */ + unsigned char *tlsext_ocsp_resp; + int tlsext_ocsp_resplen; + /* RFC4507 session ticket expected to be received or sent */ int tlsext_ticket_expected; #ifndef OPENSSL_NO_EC @@ -1317,6 +1334,15 @@ DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION) #define SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT 60 #define SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT_CB 61 #define SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT_CB_ARG 62 +#define SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB 63 +#define SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB_ARG 64 +#define SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE 65 +#define SSL_CTRL_GET_TLSEXT_STATUS_REQ_EXTS 66 +#define SSL_CTRL_SET_TLSEXT_STATUS_REQ_EXTS 67 +#define SSL_CTRL_GET_TLSEXT_STATUS_REQ_IDS 68 +#define SSL_CTRL_SET_TLSEXT_STATUS_REQ_IDS 69 +#define SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP 70 +#define SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP 71 #endif #define SSL_session_reused(ssl) \ @@ -1766,6 +1792,7 @@ void ERR_load_SSL_strings(void); #define SSL_F_SSL3_ENC 134 #define SSL_F_SSL3_GENERATE_KEY_BLOCK 238 #define SSL_F_SSL3_GET_CERTIFICATE_REQUEST 135 +#define SSL_F_SSL3_GET_CERT_STATUS 288 #define SSL_F_SSL3_GET_CERT_VERIFY 136 #define SSL_F_SSL3_GET_CLIENT_CERTIFICATE 137 #define SSL_F_SSL3_GET_CLIENT_HELLO 138 @@ -1964,6 +1991,7 @@ void ERR_load_SSL_strings(void); #define SSL_R_INVALID_CHALLENGE_LENGTH 158 #define SSL_R_INVALID_COMMAND 280 #define SSL_R_INVALID_PURPOSE 278 +#define SSL_R_INVALID_STATUS_RESPONSE 328 #define SSL_R_INVALID_TICKET_KEYS_LENGTH 325 #define SSL_R_INVALID_TRUST 279 #define SSL_R_KEY_ARG_TOO_LONG 284 @@ -2132,6 +2160,7 @@ void ERR_load_SSL_strings(void); #define SSL_R_UNSUPPORTED_ELLIPTIC_CURVE 315 #define SSL_R_UNSUPPORTED_PROTOCOL 258 #define SSL_R_UNSUPPORTED_SSL_VERSION 259 +#define SSL_R_UNSUPPORTED_STATUS_TYPE 329 #define SSL_R_WRITE_BIO_NOT_SET 260 #define SSL_R_WRONG_CIPHER_RETURNED 261 #define SSL_R_WRONG_MESSAGE_TYPE 262 diff --git a/ssl/ssl3.h b/ssl/ssl3.h index e04226f46f..646a8e6cce 100644 --- a/ssl/ssl3.h +++ b/ssl/ssl3.h @@ -545,6 +545,8 @@ typedef struct ssl3_state_st #define SSL3_ST_CR_FINISHED_B (0x1D1|SSL_ST_CONNECT) #define SSL3_ST_CR_SESSION_TICKET_A (0x1E0|SSL_ST_CONNECT) #define SSL3_ST_CR_SESSION_TICKET_B (0x1E1|SSL_ST_CONNECT) +#define SSL3_ST_CR_CERT_STATUS_A (0x1F0|SSL_ST_CONNECT) +#define SSL3_ST_CR_CERT_STATUS_B (0x1F1|SSL_ST_CONNECT) /* server */ /* extra state */ @@ -588,6 +590,8 @@ typedef struct ssl3_state_st #define SSL3_ST_SW_FINISHED_B (0x1E1|SSL_ST_ACCEPT) #define SSL3_ST_SW_SESSION_TICKET_A (0x1F0|SSL_ST_ACCEPT) #define SSL3_ST_SW_SESSION_TICKET_B (0x1F1|SSL_ST_ACCEPT) +#define SSL3_ST_SW_CERT_STATUS_A (0x200|SSL_ST_ACCEPT) +#define SSL3_ST_SW_CERT_STATUS_B (0x201|SSL_ST_ACCEPT) #define SSL3_MT_HELLO_REQUEST 0 #define SSL3_MT_CLIENT_HELLO 1 @@ -600,6 +604,7 @@ typedef struct ssl3_state_st #define SSL3_MT_CERTIFICATE_VERIFY 15 #define SSL3_MT_CLIENT_KEY_EXCHANGE 16 #define SSL3_MT_FINISHED 20 +#define SSL3_MT_CERTIFICATE_STATUS 22 #define DTLS1_MT_HELLO_VERIFY_REQUEST 3 diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 3121e0e70c..d9c0e35681 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -140,6 +140,7 @@ static ERR_STRING_DATA SSL_str_functs[]= {ERR_FUNC(SSL_F_SSL3_ENC), "SSL3_ENC"}, {ERR_FUNC(SSL_F_SSL3_GENERATE_KEY_BLOCK), "SSL3_GENERATE_KEY_BLOCK"}, {ERR_FUNC(SSL_F_SSL3_GET_CERTIFICATE_REQUEST), "SSL3_GET_CERTIFICATE_REQUEST"}, +{ERR_FUNC(SSL_F_SSL3_GET_CERT_STATUS), "SSL3_GET_CERT_STATUS"}, {ERR_FUNC(SSL_F_SSL3_GET_CERT_VERIFY), "SSL3_GET_CERT_VERIFY"}, {ERR_FUNC(SSL_F_SSL3_GET_CLIENT_CERTIFICATE), "SSL3_GET_CLIENT_CERTIFICATE"}, {ERR_FUNC(SSL_F_SSL3_GET_CLIENT_HELLO), "SSL3_GET_CLIENT_HELLO"}, @@ -341,6 +342,7 @@ static ERR_STRING_DATA SSL_str_reasons[]= {ERR_REASON(SSL_R_INVALID_CHALLENGE_LENGTH),"invalid challenge length"}, {ERR_REASON(SSL_R_INVALID_COMMAND) ,"invalid command"}, {ERR_REASON(SSL_R_INVALID_PURPOSE) ,"invalid purpose"}, +{ERR_REASON(SSL_R_INVALID_STATUS_RESPONSE),"invalid status response"}, {ERR_REASON(SSL_R_INVALID_TICKET_KEYS_LENGTH),"invalid ticket keys length"}, {ERR_REASON(SSL_R_INVALID_TRUST) ,"invalid trust"}, {ERR_REASON(SSL_R_KEY_ARG_TOO_LONG) ,"key arg too long"}, @@ -509,6 +511,7 @@ static ERR_STRING_DATA SSL_str_reasons[]= {ERR_REASON(SSL_R_UNSUPPORTED_ELLIPTIC_CURVE),"unsupported elliptic curve"}, {ERR_REASON(SSL_R_UNSUPPORTED_PROTOCOL) ,"unsupported protocol"}, {ERR_REASON(SSL_R_UNSUPPORTED_SSL_VERSION),"unsupported ssl version"}, +{ERR_REASON(SSL_R_UNSUPPORTED_STATUS_TYPE),"unsupported status type"}, {ERR_REASON(SSL_R_WRITE_BIO_NOT_SET) ,"write bio not set"}, {ERR_REASON(SSL_R_WRONG_CIPHER_RETURNED) ,"wrong cipher returned"}, {ERR_REASON(SSL_R_WRONG_MESSAGE_TYPE) ,"wrong message type"}, diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index 00a93eeee9..c5d9f0e93e 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -152,6 +152,7 @@ #include #include #include +#include #ifndef OPENSSL_NO_DH #include #endif @@ -340,6 +341,12 @@ SSL *SSL_new(SSL_CTX *ctx) s->tlsext_debug_cb = 0; s->tlsext_debug_arg = NULL; s->tlsext_ticket_expected = 0; + s->tlsext_status_type = -1; + s->tlsext_status_expected = 0; + s->tlsext_ocsp_ids = NULL; + s->tlsext_ocsp_exts = NULL; + s->tlsext_ocsp_resp = NULL; + s->tlsext_ocsp_resplen = -1; CRYPTO_add(&ctx->references,1,CRYPTO_LOCK_SSL_CTX); s->initial_ctx=ctx; #endif @@ -543,6 +550,13 @@ void SSL_free(SSL *s) if (s->tlsext_ellipticcurvelist) OPENSSL_free(s->tlsext_ellipticcurvelist); #endif /* OPENSSL_NO_EC */ if (s->tlsext_opaque_prf_input) OPENSSL_free(s->tlsext_opaque_prf_input); + if (s->tlsext_ocsp_exts) + sk_X509_EXTENSION_pop_free(s->tlsext_ocsp_exts, + X509_EXTENSION_free); + if (s->tlsext_ocsp_ids) + sk_OCSP_RESPID_pop_free(s->tlsext_ocsp_ids, OCSP_RESPID_free); + if (s->tlsext_ocsp_resp) + OPENSSL_free(s->tlsext_ocsp_resp); #endif if (s->client_CA != NULL) @@ -1556,6 +1570,9 @@ SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) || (RAND_bytes(ret->tlsext_tick_aes_key, 16) <= 0)) ret->options |= SSL_OP_NO_TICKET; + ret->tlsext_status_cb = 0; + ret->tlsext_status_arg = NULL; + #endif #ifndef OPENSSL_NO_PSK ret->psk_identity_hint=NULL; diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 50a8acc015..cb93ff2188 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -829,6 +829,7 @@ int ssl3_put_cipher_by_char(const SSL_CIPHER *c,unsigned char *p); void ssl3_init_finished_mac(SSL *s); int ssl3_send_server_certificate(SSL *s); int ssl3_send_newsession_ticket(SSL *s); +int ssl3_send_cert_status(SSL *s); int ssl3_get_finished(SSL *s,int state_a,int state_b); int ssl3_setup_key_block(SSL *s); int ssl3_send_change_cipher_spec(SSL *s,int state_a,int state_b); @@ -921,6 +922,7 @@ int ssl3_client_hello(SSL *s); int ssl3_get_server_hello(SSL *s); int ssl3_get_certificate_request(SSL *s); int ssl3_get_new_session_ticket(SSL *s); +int ssl3_get_cert_status(SSL *s); int ssl3_get_server_done(SSL *s); int ssl3_send_client_verify(SSL *s); int ssl3_send_client_certificate(SSL *s); diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c index 3509f62b18..4aa7600fe7 100644 --- a/ssl/t1_enc.c +++ b/ssl/t1_enc.c @@ -899,7 +899,7 @@ int tls1_generate_master_secret(SSL *s, unsigned char *out, unsigned char *p, { unsigned char buff[SSL_MAX_MASTER_KEY_LENGTH]; const void *co = NULL, *so = NULL; - int col = 0, sol = NULL; + int col = 0, sol = 0; #ifdef KSSL_DEBUG printf ("tls1_generate_master_secret(%p,%p, %p, %d)\n", s,out, p,len); diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 1aaf8905c8..476e4240e5 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -113,6 +113,7 @@ #include #include #include +#include #include "ssl_locl.h" const char tls1_version_str[]="TLSv1" OPENSSL_VERSION_PTEXT; @@ -387,6 +388,54 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha } #endif + if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp) + { + int i; + long extlen, idlen, itmp; + OCSP_RESPID *id; + + idlen = 0; + for (i = 0; i < sk_OCSP_RESPID_num(s->tlsext_ocsp_ids); i++) + { + id = sk_OCSP_RESPID_value(s->tlsext_ocsp_ids, i); + itmp = i2d_OCSP_RESPID(id, NULL); + if (itmp <= 0) + return NULL; + idlen += itmp + 2; + } + + if (s->tlsext_ocsp_exts) + { + extlen = i2d_X509_EXTENSIONS(s->tlsext_ocsp_exts, NULL); + if (extlen < 0) + return NULL; + } + else + extlen = 0; + + if ((long)(limit - ret - 7 - extlen - idlen) < 0) return NULL; + s2n(TLSEXT_TYPE_status_request, ret); + if (extlen + idlen > 0xFFF0) + return NULL; + s2n(extlen + idlen + 5, ret); + *(ret++) = TLSEXT_STATUSTYPE_ocsp; + s2n(idlen, ret); + for (i = 0; i < sk_OCSP_RESPID_num(s->tlsext_ocsp_ids); i++) + { + /* save position of id len */ + unsigned char *q = ret; + id = sk_OCSP_RESPID_value(s->tlsext_ocsp_ids, i); + /* skip over id len */ + ret += 2; + itmp = i2d_OCSP_RESPID(id, &ret); + /* write id len */ + s2n(itmp, q); + } + s2n(extlen, ret); + if (extlen > 0) + i2d_X509_EXTENSIONS(s->tlsext_ocsp_exts, &ret); + } + if ((extdatalen = ret-p-2)== 0) return p; @@ -432,7 +481,7 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha } /* Currently the server should not respond with a SupportedCurves extension */ #endif /* OPENSSL_NO_EC */ - + if (s->tlsext_ticket_expected && !(SSL_get_options(s) & SSL_OP_NO_TICKET)) { @@ -441,6 +490,13 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha s2n(0,ret); } + if (s->tlsext_status_expected) + { + if ((long)(limit - ret - 4) < 0) return NULL; + s2n(TLSEXT_TYPE_status_request,ret); + s2n(0,ret); + } + #ifdef TLSEXT_TYPE_opaque_prf_input if (s->s3->server_opaque_prf_input != NULL) { @@ -473,6 +529,7 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in unsigned short len; unsigned char *data = *p; s->servername_done = 0; + s->tlsext_status_type = -1; if (data >= (d+n-2)) return 1; @@ -675,6 +732,106 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in } } #endif + else if (type == TLSEXT_TYPE_status_request + && s->ctx->tlsext_status_cb) + { + + if (size < 5) + { + *al = SSL_AD_DECODE_ERROR; + return 0; + } + + s->tlsext_status_type = *data++; + size--; + if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp) + { + const unsigned char *sdata; + int dsize; + /* Read in responder_id_list */ + n2s(data,dsize); + size -= 2; + if (dsize > size ) + { + *al = SSL_AD_DECODE_ERROR; + return 0; + } + while (dsize > 0) + { + OCSP_RESPID *id; + int idsize; + if (dsize < 4) + { + *al = SSL_AD_DECODE_ERROR; + return 0; + } + n2s(data, idsize); + dsize -= 2 + idsize; + if (dsize < 0) + { + *al = SSL_AD_DECODE_ERROR; + return 0; + } + sdata = data; + data += idsize; + id = d2i_OCSP_RESPID(NULL, + &sdata, idsize); + if (!id) + { + *al = SSL_AD_DECODE_ERROR; + return 0; + } + if (data != sdata) + { + OCSP_RESPID_free(id); + *al = SSL_AD_DECODE_ERROR; + return 0; + } + if (!s->tlsext_ocsp_ids + && !(s->tlsext_ocsp_ids = + sk_OCSP_RESPID_new_null())) + { + OCSP_RESPID_free(id); + *al = SSL_AD_INTERNAL_ERROR; + return 0; + } + if (!sk_OCSP_RESPID_push( + s->tlsext_ocsp_ids, id)) + { + OCSP_RESPID_free(id); + *al = SSL_AD_INTERNAL_ERROR; + return 0; + } + } + + /* Read in request_extensions */ + n2s(data,dsize); + size -= 2; + if (dsize > size) + { + *al = SSL_AD_DECODE_ERROR; + return 0; + } + sdata = data; + if (dsize > 0) + { + s->tlsext_ocsp_exts = + d2i_X509_EXTENSIONS(NULL, + &sdata, dsize); + if (!s->tlsext_ocsp_exts + || (data + dsize != sdata)) + { + *al = SSL_AD_DECODE_ERROR; + return 0; + } + } + } + /* We don't know what to do with any other type + * so ignore it. + */ + else + s->tlsext_status_type = -1; + } /* session ticket processed earlier */ data+=size; @@ -791,6 +948,19 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in } } #endif + else if (type == TLSEXT_TYPE_status_request) + { + /* MUST be empty and only sent if we've requested + * a status request message. + */ + if ((s->tlsext_status_type == -1) || (size > 0)) + { + *al = TLS1_AD_UNSUPPORTED_EXTENSION; + return 0; + } + /* Set flag to expect CertificateStatus message */ + s->tlsext_status_expected = 1; + } data+=size; } @@ -822,6 +992,35 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in } } + /* If we've requested certificate status and we wont get one + * tell the callback + */ + if ((s->tlsext_status_type != -1) && !(s->tlsext_status_expected) + && s->ctx->tlsext_status_cb) + { + int r; + /* Set resp to NULL, resplen to -1 so callback knows + * there is no response. + */ + if (s->tlsext_ocsp_resp) + { + OPENSSL_free(s->tlsext_ocsp_resp); + s->tlsext_ocsp_resp = NULL; + } + s->tlsext_ocsp_resplen = -1; + r = s->ctx->tlsext_status_cb(s, s->ctx->tlsext_status_arg); + if (r == 0) + { + *al = SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE; + return 0; + } + if (r < 0) + { + *al = SSL_AD_INTERNAL_ERROR; + return 0; + } + } + *p = data; return 1; } @@ -966,6 +1165,36 @@ int ssl_check_clienthello_tlsext(SSL *s) else if (s->initial_ctx != NULL && s->initial_ctx->tlsext_servername_callback != 0) ret = s->initial_ctx->tlsext_servername_callback(s, &al, s->initial_ctx->tlsext_servername_arg); + /* If status request then ask callback what to do. + * Note: this must be called after servername callbacks in case + * the certificate has changed. + */ + if ((s->tlsext_status_type != -1) && s->ctx->tlsext_status_cb) + { + int r; + r = s->ctx->tlsext_status_cb(s, s->ctx->tlsext_status_arg); + switch (r) + { + /* We don't want to send a status request response */ + case SSL_TLSEXT_ERR_NOACK: + s->tlsext_status_expected = 0; + break; + /* status request response should be sent */ + case SSL_TLSEXT_ERR_OK: + if (s->tlsext_ocsp_resp) + s->tlsext_status_expected = 1; + else + s->tlsext_status_expected = 0; + break; + /* something bad happened */ + case SSL_TLSEXT_ERR_ALERT_FATAL: + ret = SSL_TLSEXT_ERR_ALERT_FATAL; + al = SSL_AD_INTERNAL_ERROR; + goto err; + } + } + else + s->tlsext_status_expected = 0; #ifdef TLSEXT_TYPE_opaque_prf_input { @@ -1022,8 +1251,8 @@ int ssl_check_clienthello_tlsext(SSL *s) al = SSL_AD_HANDSHAKE_FAILURE; } } -#endif +#endif err: switch (ret) { diff --git a/ssl/tls1.h b/ssl/tls1.h index cac8d0ea22..d831eecd87 100644 --- a/ssl/tls1.h +++ b/ssl/tls1.h @@ -203,6 +203,8 @@ extern "C" { /* NameType value from RFC 3546 */ #define TLSEXT_NAMETYPE_host_name 0 +/* status request value from RFC 3546 */ +#define TLSEXT_STATUSTYPE_ocsp 1 /* ECPointFormat values from draft-ietf-tls-ecc-12 */ #define TLSEXT_ECPOINTFORMAT_first 0 @@ -227,12 +229,33 @@ SSL_callback_ctrl(ssl,SSL_CTRL_SET_TLSEXT_DEBUG_CB,(void (*)(void))cb) #define SSL_set_tlsext_debug_arg(ssl, arg) \ SSL_ctrl(ssl,SSL_CTRL_SET_TLSEXT_DEBUG_ARG,0, (void *)arg) +#define SSL_set_tlsext_status_type(ssl, type) \ +SSL_ctrl(ssl,SSL_CTRL_SET_TLSEXT_STATUS_REQ_TYPE,type, NULL) + +#define SSL_get_tlsext_status_exts(ssl, arg) \ +SSL_ctrl(ssl,SSL_CTRL_GET_TLSEXT_STATUS_REQ_EXTS,0, (void *)arg) + +#define SSL_set_tlsext_status_exts(ssl, arg) \ +SSL_ctrl(ssl,SSL_CTRL_SET_TLSEXT_STATUS_REQ_EXTS,0, (void *)arg) + +#define SSL_get_tlsext_status_ids(ssl, arg) \ +SSL_ctrl(ssl,SSL_CTRL_GET_TLSEXT_STATUS_REQ_IDS,0, (void *)arg) + +#define SSL_set_tlsext_status_ids(ssl, arg) \ +SSL_ctrl(ssl,SSL_CTRL_SET_TLSEXT_STATUS_REQ_IDS,0, (void *)arg) + +#define SSL_get_tlsext_status_ocsp_resp(ssl, arg) \ +SSL_ctrl(ssl,SSL_CTRL_GET_TLSEXT_STATUS_REQ_OCSP_RESP,0, (void *)arg) + +#define SSL_set_tlsext_status_ocsp_resp(ssl, arg, arglen) \ +SSL_ctrl(ssl,SSL_CTRL_SET_TLSEXT_STATUS_REQ_OCSP_RESP,arglen, (void *)arg) + #define SSL_CTX_set_tlsext_servername_callback(ctx, cb) \ SSL_CTX_callback_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_CB,(void (*)(void))cb) -#define SSL_TLSEXT_ERR_OK 0 -#define SSL_TLSEXT_ERR_ALERT_WARNING 1 -#define SSL_TLSEXT_ERR_ALERT_FATAL 2 +#define SSL_TLSEXT_ERR_OK 0 +#define SSL_TLSEXT_ERR_ALERT_WARNING 1 +#define SSL_TLSEXT_ERR_ALERT_FATAL 2 #define SSL_TLSEXT_ERR_NOACK 3 #define SSL_CTX_set_tlsext_servername_arg(ctx, arg) \ @@ -243,6 +266,12 @@ SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG,0, (void *)arg) #define SSL_CTX_set_tlsext_ticket_keys(ctx, keys, keylen) \ SSL_CTX_ctrl((ctx),SSL_CTRL_SET_TLXEXT_TICKET_KEYS,(keylen),(keys)) +#define SSL_CTX_set_tlsext_status_cb(ssl, cb) \ +SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB,(void (*)(void))cb) + +#define SSL_CTX_set_tlsext_status_arg(ssl, arg) \ +SSL_CTX_ctrl(ssl,SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB_ARG,0, (void *)arg) + #define SSL_set_tlsext_opaque_prf_input(s, src, len) \ SSL_ctrl(s,SSL_CTRL_SET_TLSEXT_OPAQUE_PRF_INPUT, len, src) #define SSL_CTX_set_tlsext_opaque_prf_input_callback(ctx, cb) \