From a523276786b8f8ae9ab331a19deeef71a2e463dc Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Fri, 12 Oct 2007 00:00:36 +0000 Subject: [PATCH] Backport certificate status request TLS extension support to 0.9.8. --- CHANGES | 7 +- apps/apps.h | 15 ++ apps/ocsp.c | 210 ++++++++++++++---- apps/s_client.c | 48 ++++ apps/s_server.c | 182 +++++++++++++++ apps/x509.c | 11 +- crypto/asn1/x_exten.c | 5 + crypto/ocsp/ocsp.h | 17 +- crypto/ocsp/ocsp_err.c | 3 +- crypto/ocsp/ocsp_ht.c | 468 ++++++++++++++++++++++++++++++++------- crypto/ossl_typ.h | 4 + crypto/stack/safestack.h | 22 ++ crypto/x509/x509.h | 3 + crypto/x509v3/v3_utl.c | 24 ++ crypto/x509v3/x509v3.h | 1 + ssl/s23_clnt.c | 2 + ssl/s3_clnt.c | 94 +++++++- ssl/s3_lib.c | 49 ++++ ssl/s3_srvr.c | 56 +++++ ssl/ssl.h | 31 +++ ssl/ssl3.h | 5 + ssl/ssl_err.c | 3 + ssl/ssl_lib.c | 17 ++ ssl/ssl_locl.h | 2 + ssl/t1_lib.c | 232 ++++++++++++++++++- ssl/tls1.h | 36 ++- util/libeay.num | 8 + 27 files changed, 1408 insertions(+), 147 deletions(-) diff --git a/CHANGES b/CHANGES index 141cce8776..a3a713a7ac 100644 --- a/CHANGES +++ b/CHANGES @@ -4,7 +4,12 @@ Changes between 0.9.8f and 0.9.8g [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] Changes between 0.9.8e and 0.9.8f [11 Oct 2007] diff --git a/apps/apps.h b/apps/apps.h index 26dcbc5771..0df170813a 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); @@ -228,6 +231,12 @@ extern BIO *bio_err; # endif #endif +#ifdef OPENSSL_SYSNAME_WIN32 +# define openssl_fdset(a,b) FD_SET((unsigned int)a, b) +#else +# define openssl_fdset(a,b) FD_SET(a, b) +#endif + typedef struct args_st { char **data; @@ -275,6 +284,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 3dc36c4bdc..df0339b743 100644 --- a/apps/ocsp.c +++ b/apps/ocsp.c @@ -58,13 +58,14 @@ #ifndef OPENSSL_NO_OCSP #include +#include #include -#include "apps.h" -#include +#include +#include #include -#include +#include #include -#include +#include "apps.h" /* Maximum leeway in validity period: default 5 minutes */ #define MAX_VALIDITY_PERIOD (5 * 60) @@ -86,6 +87,8 @@ static char **lookup_serial(CA_DB *db, ASN1_INTEGER *ser); static BIO *init_responder(char *port); static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio, char *port); static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp); +static OCSP_RESPONSE *query_responder(BIO *err, BIO *cbio, char *path, + OCSP_REQUEST *req, int req_timeout); #undef PROG #define PROG ocsp_main @@ -112,11 +115,11 @@ int MAIN(int argc, char **argv) BIO *acbio = NULL, *cbio = NULL; BIO *derbio = NULL; BIO *out = NULL; + int req_timeout = -1; int req_text = 0, resp_text = 0; 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; @@ -154,6 +157,22 @@ int MAIN(int argc, char **argv) } else badarg = 1; } + else if (!strcmp(*args, "-timeout")) + { + if (args[1]) + { + args++; + req_timeout = atol(*args); + if (req_timeout < 0) + { + BIO_printf(bio_err, + "Illegal timeout value %s\n", + *args); + badarg = 1; + } + } + else badarg = 1; + } else if (!strcmp(*args, "-url")) { if (args[1]) @@ -703,52 +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); - } - if (BIO_do_connect(cbio) <= 0) - { - BIO_printf(bio_err, "Error connecting BIO\n"); - goto end; - } - resp = OCSP_sendreq_bio(cbio, path, req); - BIO_free_all(cbio); - cbio = NULL; - if (!resp) - { - BIO_printf(bio_err, "Error querying OCSP responsder\n"); - goto end; - } } else if (respin) { @@ -897,7 +878,6 @@ end: OPENSSL_free(host); OPENSSL_free(port); OPENSSL_free(path); - SSL_CTX_free(ctx); } OPENSSL_EXIT(ret); @@ -1121,6 +1101,7 @@ static char **lookup_serial(CA_DB *db, ASN1_INTEGER *ser) char *itmp, *row[DB_NUMBER],**rrow; for (i = 0; i < DB_NUMBER; i++) row[i] = NULL; bn = ASN1_INTEGER_to_BN(ser,NULL); + OPENSSL_assert(bn); /* FIXME: should report an error at this point and abort */ if (BN_is_zero(bn)) itmp = BUF_strdup("00"); else @@ -1231,4 +1212,137 @@ static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp) return 1; } +static OCSP_RESPONSE *query_responder(BIO *err, BIO *cbio, char *path, + OCSP_REQUEST *req, int req_timeout) + { + int fd; + int rv; + OCSP_REQ_CTX *ctx = NULL; + OCSP_RESPONSE *rsp = NULL; + fd_set confds; + struct timeval tv; + + if (req_timeout != -1) + BIO_set_nbio(cbio, 1); + + rv = BIO_do_connect(cbio); + + if ((rv <= 0) && ((req_timeout == -1) || !BIO_should_retry(cbio))) + { + BIO_puts(err, "Error connecting BIO\n"); + return NULL; + } + + if (req_timeout == -1) + return OCSP_sendreq_bio(cbio, path, req); + + if (BIO_get_fd(cbio, &fd) <= 0) + { + BIO_puts(err, "Can't get connection fd\n"); + goto err; + } + + if (rv <= 0) + { + FD_ZERO(&confds); + openssl_fdset(fd, &confds); + tv.tv_usec = 0; + tv.tv_sec = req_timeout; + rv = select(fd + 1, NULL, (void *)&confds, NULL, &tv); + if (rv == 0) + { + BIO_puts(err, "Timeout on connect\n"); + return NULL; + } + } + + + ctx = OCSP_sendreq_new(cbio, path, req, -1); + if (!ctx) + return NULL; + + for (;;) + { + rv = OCSP_sendreq_nbio(&rsp, ctx); + if (rv != -1) + break; + FD_ZERO(&confds); + openssl_fdset(fd, &confds); + tv.tv_usec = 0; + tv.tv_sec = req_timeout; + if (BIO_should_read(cbio)) + rv = select(fd + 1, (void *)&confds, NULL, NULL, &tv); + else if (BIO_should_write(cbio)) + rv = select(fd + 1, NULL, (void *)&confds, NULL, &tv); + else + { + BIO_puts(err, "Unexpected retry condition\n"); + goto err; + } + if (rv == 0) + { + BIO_puts(err, "Timeout on request\n"); + break; + } + if (rv == -1) + { + BIO_puts(err, "Select error\n"); + break; + } + + } + err: + if (ctx) + OCSP_REQ_CTX_free(ctx); + + 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 d240fe2a98..f444d27a9f 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -134,6 +134,7 @@ typedef unsigned int u_int; #include #include #include +#include #include "s_apps.h" #include "timeouts.h" @@ -173,12 +174,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; @@ -239,6 +242,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 } @@ -435,6 +439,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) @@ -826,6 +832,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); @@ -1430,3 +1453,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 7c5775f81c..2b4e256c1a 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -153,6 +153,7 @@ typedef unsigned int u_int; #include #include #include +#include #ifndef OPENSSL_NO_DH #include #endif @@ -269,6 +270,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; @@ -585,6 +588,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 **); @@ -792,6 +941,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; } @@ -1430,6 +1606,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 5f61eb5c46..f6938356f8 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 8163411c92..a0577a717e 100644 --- a/crypto/ocsp/ocsp.h +++ b/crypto/ocsp/ocsp.h @@ -186,11 +186,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, @@ -198,14 +198,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) */ @@ -397,6 +401,10 @@ typedef struct ocsp_service_locator_st (char *(*)())d2i_OCSP_CERTSTATUS,(char *)(cs)) OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, char *path, OCSP_REQUEST *req); +OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, char *path, OCSP_REQUEST *req, + int maxline); +int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx); +void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx); OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *dgst, X509 *subject, X509 *issuer); @@ -574,6 +582,7 @@ void ERR_load_OCSP_strings(void); #define OCSP_F_OCSP_REQUEST_VERIFY 116 #define OCSP_F_OCSP_RESPONSE_GET1_BASIC 111 #define OCSP_F_OCSP_SENDREQ_BIO 112 +#define OCSP_F_PARSE_HTTP_LINE1 117 #define OCSP_F_REQUEST_VERIFY 113 /* Reason codes. */ diff --git a/crypto/ocsp/ocsp_err.c b/crypto/ocsp/ocsp_err.c index ad62364f29..d2f2e79f44 100644 --- a/crypto/ocsp/ocsp_err.c +++ b/crypto/ocsp/ocsp_err.c @@ -1,6 +1,6 @@ /* crypto/ocsp/ocsp_err.c */ /* ==================================================================== - * Copyright (c) 1999-2005 The OpenSSL Project. All rights reserved. + * Copyright (c) 1999-2007 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -86,6 +86,7 @@ static ERR_STRING_DATA OCSP_str_functs[]= {ERR_FUNC(OCSP_F_OCSP_REQUEST_VERIFY), "OCSP_request_verify"}, {ERR_FUNC(OCSP_F_OCSP_RESPONSE_GET1_BASIC), "OCSP_response_get1_basic"}, {ERR_FUNC(OCSP_F_OCSP_SENDREQ_BIO), "OCSP_sendreq_bio"}, +{ERR_FUNC(OCSP_F_PARSE_HTTP_LINE1), "PARSE_HTTP_LINE1"}, {ERR_FUNC(OCSP_F_REQUEST_VERIFY), "REQUEST_VERIFY"}, {0,NULL} }; diff --git a/crypto/ocsp/ocsp_ht.c b/crypto/ocsp/ocsp_ht.c index 9213e58ae4..a8e569b74a 100644 --- a/crypto/ocsp/ocsp_ht.c +++ b/crypto/ocsp/ocsp_ht.c @@ -1,9 +1,9 @@ /* ocsp_ht.c */ /* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL - * project 2000. + * project 2006. */ /* ==================================================================== - * Copyright (c) 2000 The OpenSSL Project. All rights reserved. + * Copyright (c) 2006 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -68,106 +68,404 @@ #define strtoul (unsigned long)strtol #endif /* OPENSSL_SYS_SUNOS */ -/* Quick and dirty HTTP OCSP request handler. - * Could make this a bit cleverer by adding - * support for non blocking BIOs and a few - * other refinements. - */ +/* Stateful OCSP request code, supporting non-blocking I/O */ -OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, char *path, OCSP_REQUEST *req) -{ - BIO *mem = NULL; - char tmpbuf[1024]; - OCSP_RESPONSE *resp = NULL; - char *p, *q, *r; - int len, retcode; - static char req_txt[] = -"POST %s HTTP/1.0\r\n\ -Content-Type: application/ocsp-request\r\n\ -Content-Length: %d\r\n\r\n"; - - len = i2d_OCSP_REQUEST(req, NULL); - if(BIO_printf(b, req_txt, path, len) < 0) { - OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_WRITE_ERROR); - goto err; - } - if(i2d_OCSP_REQUEST_bio(b, req) <= 0) { - OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_WRITE_ERROR); - goto err; +/* Opaque OCSP request status structure */ + +struct ocsp_req_ctx_st { + int state; /* Current I/O state */ + unsigned char *iobuf; /* Line buffer */ + int iobuflen; /* Line buffer length */ + BIO *io; /* BIO to perform I/O with */ + BIO *mem; /* Memory BIO response is built into */ + unsigned long asn1_len; /* ASN1 length of response */ + }; + +#define OCSP_MAX_REQUEST_LENGTH (100 * 1024) +#define OCSP_MAX_LINE_LEN 4096; + +/* OCSP states */ + +/* If set no reading should be performed */ +#define OHS_NOREAD 0x1000 +/* Error condition */ +#define OHS_ERROR (0 | OHS_NOREAD) +/* First line being read */ +#define OHS_FIRSTLINE 1 +/* MIME headers being read */ +#define OHS_HEADERS 2 +/* OCSP initial header (tag + length) being read */ +#define OHS_ASN1_HEADER 3 +/* OCSP content octets being read */ +#define OHS_ASN1_CONTENT 4 +/* Request being sent */ +#define OHS_ASN1_WRITE (6 | OHS_NOREAD) +/* Request being flushed */ +#define OHS_ASN1_FLUSH (7 | OHS_NOREAD) +/* Completed */ +#define OHS_DONE (8 | OHS_NOREAD) + + +static int parse_http_line1(char *line); + +void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx) + { + if (rctx->mem) + BIO_free(rctx->mem); + if (rctx->iobuf) + OPENSSL_free(rctx->iobuf); + OPENSSL_free(rctx); } - if(!(mem = BIO_new(BIO_s_mem()))) goto err; - /* Copy response to a memory BIO: socket bios can't do gets! */ - while ((len = BIO_read(b, tmpbuf, sizeof tmpbuf))) { - if(len < 0) { - OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_READ_ERROR); - goto err; + +OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, char *path, OCSP_REQUEST *req, + int maxline) + { + static char post_hdr[] = "POST %s HTTP/1.0\r\n" + "Content-Type: application/ocsp-request\r\n" + "Content-Length: %d\r\n\r\n"; + + OCSP_REQ_CTX *rctx; + rctx = OPENSSL_malloc(sizeof(OCSP_REQ_CTX)); + rctx->state = OHS_FIRSTLINE; + rctx->mem = BIO_new(BIO_s_mem()); + rctx->io = io; + if (maxline > 0) + rctx->iobuflen = maxline; + else + rctx->iobuflen = OCSP_MAX_LINE_LEN; + rctx->iobuf = OPENSSL_malloc(rctx->iobuflen); + if (!path) + path = "/"; + + if (BIO_printf(rctx->mem, post_hdr, path, + i2d_OCSP_REQUEST(req, NULL)) <= 0) + { + rctx->state = OHS_ERROR; + return 0; } - BIO_write(mem, tmpbuf, len); - } - if(BIO_gets(mem, tmpbuf, 512) <= 0) { - OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_RESPONSE_PARSE_ERROR); - goto err; + if (i2d_OCSP_REQUEST_bio(rctx->mem, req) <= 0) + { + rctx->state = OHS_ERROR; + return 0; + } + rctx->state = OHS_ASN1_WRITE; + rctx->asn1_len = BIO_get_mem_data(rctx->mem, NULL); + + return rctx; } - /* Parse the HTTP response. This will look like this: - * "HTTP/1.0 200 OK". We need to obtain the numeric code and - * (optional) informational message. - */ +/* Parse the HTTP response. This will look like this: + * "HTTP/1.0 200 OK". We need to obtain the numeric code and + * (optional) informational message. + */ + +static int parse_http_line1(char *line) + { + int retcode; + char *p, *q, *r; /* Skip to first white space (passed protocol info) */ - for(p = tmpbuf; *p && !isspace((unsigned char)*p); p++) continue; - if(!*p) { - OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_RESPONSE_PARSE_ERROR); - goto err; - } + + for(p = line; *p && !isspace((unsigned char)*p); p++) + continue; + if(!*p) + { + OCSPerr(OCSP_F_PARSE_HTTP_LINE1, + OCSP_R_SERVER_RESPONSE_PARSE_ERROR); + return 0; + } + /* Skip past white space to start of response code */ - while(*p && isspace((unsigned char)*p)) p++; - if(!*p) { - OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_RESPONSE_PARSE_ERROR); - goto err; - } + while(*p && isspace((unsigned char)*p)) + p++; + + if(!*p) + { + OCSPerr(OCSP_F_PARSE_HTTP_LINE1, + OCSP_R_SERVER_RESPONSE_PARSE_ERROR); + return 0; + } + /* Find end of response code: first whitespace after start of code */ - for(q = p; *q && !isspace((unsigned char)*q); q++) continue; - if(!*q) { - OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_RESPONSE_PARSE_ERROR); - goto err; - } + for(q = p; *q && !isspace((unsigned char)*q); q++) + continue; + + if(!*q) + { + OCSPerr(OCSP_F_PARSE_HTTP_LINE1, + OCSP_R_SERVER_RESPONSE_PARSE_ERROR); + return 0; + } + /* Set end of response code and start of message */ *q++ = 0; + /* Attempt to parse numeric code */ retcode = strtoul(p, &r, 10); - if(*r) goto err; + + if(*r) + return 0; + /* Skip over any leading white space in message */ - while(*q && isspace((unsigned char)*q)) q++; - if(*q) { - /* Finally zap any trailing white space in message (include CRLF) */ - /* We know q has a non white space character so this is OK */ - for(r = q + strlen(q) - 1; isspace((unsigned char)*r); r--) *r = 0; - } - if(retcode != 200) { - OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_RESPONSE_ERROR); - if(!*q) { - ERR_add_error_data(2, "Code=", p); + while(*q && isspace((unsigned char)*q)) + q++; + + if(*q) + { + /* Finally zap any trailing white space in message (include + * CRLF) */ + + /* We know q has a non white space character so this is OK */ + for(r = q + strlen(q) - 1; isspace((unsigned char)*r); r--) + *r = 0; } - else { + if(retcode != 200) + { + OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_ERROR); + if(!*q) + ERR_add_error_data(2, "Code=", p); + else ERR_add_error_data(4, "Code=", p, ",Reason=", q); + return 0; } - goto err; + + + return 1; + } - /* Find blank line marking beginning of content */ - while(BIO_gets(mem, tmpbuf, 512) > 0) + +int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx) { - for(p = tmpbuf; *p && isspace((unsigned char)*p); p++) continue; - if(!*p) break; - } - if(*p) { - OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_NO_CONTENT); - goto err; + int i, n; + const unsigned char *p; + next_io: + if (!(rctx->state & OHS_NOREAD)) + { + n = BIO_read(rctx->io, rctx->iobuf, rctx->iobuflen); + + if (n <= 0) + { + if (BIO_should_retry(rctx->io)) + return -1; + return 0; + } + + /* Write data to memory BIO */ + + if (BIO_write(rctx->mem, rctx->iobuf, n) != n) + return 0; + } + + switch(rctx->state) + { + + case OHS_ASN1_WRITE: + n = BIO_get_mem_data(rctx->mem, &p); + + i = BIO_write(rctx->io, + p + (n - rctx->asn1_len), rctx->asn1_len); + + if (i <= 0) + { + if (BIO_should_retry(rctx->io)) + return -1; + rctx->state = OHS_ERROR; + return 0; + } + + rctx->asn1_len -= i; + + if (rctx->asn1_len > 0) + goto next_io; + + rctx->state = OHS_ASN1_FLUSH; + + (void)BIO_reset(rctx->mem); + + case OHS_ASN1_FLUSH: + + i = BIO_flush(rctx->io); + + if (i > 0) + { + rctx->state = OHS_FIRSTLINE; + goto next_io; + } + + if (BIO_should_retry(rctx->io)) + return -1; + + rctx->state = OHS_ERROR; + return 0; + + case OHS_ERROR: + return 0; + + case OHS_FIRSTLINE: + case OHS_HEADERS: + + /* Attempt to read a line in */ + + next_line: + /* Due to &%^*$" memory BIO behaviour with BIO_gets we + * have to check there's a complete line in there before + * calling BIO_gets or we'll just get a partial read. + */ + n = BIO_get_mem_data(rctx->mem, &p); + if ((n <= 0) || !memchr(p, '\n', n)) + { + if (n >= rctx->iobuflen) + { + rctx->state = OHS_ERROR; + return 0; + } + goto next_io; + } + n = BIO_gets(rctx->mem, (char *)rctx->iobuf, rctx->iobuflen); + + if (n <= 0) + { + if (BIO_should_retry(rctx->mem)) + goto next_io; + rctx->state = OHS_ERROR; + return 0; + } + + /* Don't allow excessive lines */ + if (n == rctx->iobuflen) + { + rctx->state = OHS_ERROR; + return 0; + } + + /* First line */ + if (rctx->state == OHS_FIRSTLINE) + { + if (parse_http_line1((char *)rctx->iobuf)) + { + rctx->state = OHS_HEADERS; + goto next_line; + } + else + { + rctx->state = OHS_ERROR; + return 0; + } + } + else + { + /* Look for blank line: end of headers */ + for (p = rctx->iobuf; *p; p++) + { + if ((*p != '\r') && (*p != '\n')) + break; + } + if (*p) + goto next_line; + + rctx->state = OHS_ASN1_HEADER; + + } + + /* Fall thru */ + + + case OHS_ASN1_HEADER: + /* Now reading ASN1 header: can read at least 6 bytes which + * is more than enough for any valid ASN1 SEQUENCE header + */ + n = BIO_get_mem_data(rctx->mem, &p); + if (n < 6) + goto next_io; + + /* Check it is an ASN1 SEQUENCE */ + if (*p++ != (V_ASN1_SEQUENCE|V_ASN1_CONSTRUCTED)) + { + rctx->state = OHS_ERROR; + return 0; + } + + /* Check out length field */ + if (*p & 0x80) + { + n = *p & 0x7F; + /* Not NDEF or excessive length */ + if (!n || (n > 4)) + { + rctx->state = OHS_ERROR; + return 0; + } + p++; + rctx->asn1_len = 0; + for (i = 0; i < n; i++) + { + rctx->asn1_len <<= 8; + rctx->asn1_len |= *p++; + } + + if (rctx->asn1_len > OCSP_MAX_REQUEST_LENGTH) + { + rctx->state = OHS_ERROR; + return 0; + } + + rctx->asn1_len += n + 2; + } + else + rctx->asn1_len = *p + 2; + + rctx->state = OHS_ASN1_CONTENT; + + /* Fall thru */ + + case OHS_ASN1_CONTENT: + n = BIO_get_mem_data(rctx->mem, &p); + if (n < (int)rctx->asn1_len) + goto next_io; + + + *presp = d2i_OCSP_RESPONSE(NULL, &p, rctx->asn1_len); + if (*presp) + { + rctx->state = OHS_DONE; + return 1; + } + + rctx->state = OHS_ERROR; + return 0; + + break; + + case OHS_DONE: + return 1; + + } + + + + return 0; + + } - if(!(resp = d2i_OCSP_RESPONSE_bio(mem, NULL))) { - OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,ERR_R_NESTED_ASN1_ERROR); - goto err; + +/* Blocking OCSP request handler: now a special case of non-blocking I/O */ + +OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, char *path, OCSP_REQUEST *req) + { + OCSP_RESPONSE *resp = NULL; + OCSP_REQ_CTX *ctx; + int rv; + + ctx = OCSP_sendreq_new(b, path, req, -1); + + do + { + rv = OCSP_sendreq_nbio(&resp, ctx); + } while ((rv == -1) && BIO_should_retry(b)); + + OCSP_REQ_CTX_free(ctx); + + if (rv) + return resp; + + return NULL; } - err: - BIO_free(mem); - return resp; -} diff --git a/crypto/ossl_typ.h b/crypto/ossl_typ.h index 9c335a1819..08c2968f12 100644 --- a/crypto/ossl_typ.h +++ b/crypto/ossl_typ.h @@ -171,4 +171,8 @@ typedef void CRYPTO_EX_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, 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 53df649ecf..c3b7ed53db 100644 --- a/crypto/stack/safestack.h +++ b/crypto/stack/safestack.h @@ -964,6 +964,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 16a954f709..c431237ec7 100644 --- a/crypto/x509/x509.h +++ b/crypto/x509/x509.h @@ -203,6 +203,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) @@ -918,6 +920,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/x509v3/v3_utl.c b/crypto/x509v3/v3_utl.c index 3dba0557b8..ac171ca940 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 91d2fb5b8b..db2b0482c1 100644 --- a/crypto/x509v3/x509v3.h +++ b/crypto/x509v3/x509v3.h @@ -617,6 +617,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 a596e7b2ec..c45a8e0a04 100644 --- a/ssl/s23_clnt.c +++ b/ssl/s23_clnt.c @@ -230,6 +230,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; } #endif diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c index 4d4d63a6f1..4e7846bb4c 100644 --- a/ssl/s3_clnt.c +++ b/ssl/s3_clnt.c @@ -283,10 +283,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; @@ -450,6 +463,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: @@ -1688,7 +1709,7 @@ int ssl3_get_new_session_ticket(SSL *s) if (ticklen + 6 != n) { al = SSL3_AL_FATAL,SSL_AD_DECODE_ERROR; - SSLerr(SSL_F_SSL3_NEW_SESSION_TICKET,SSL_R_LENGTH_MISMATCH); + SSLerr(SSL_F_SSL3_GET_NEW_SESSION_TICKET,SSL_R_LENGTH_MISMATCH); goto f_err; } if (s->session->tlsext_tick) @@ -1699,7 +1720,7 @@ int ssl3_get_new_session_ticket(SSL *s) s->session->tlsext_tick = OPENSSL_malloc(ticklen); if (!s->session->tlsext_tick) { - SSLerr(SSL_F_SSL3_NEW_SESSION_TICKET,ERR_R_MALLOC_FAILURE); + SSLerr(SSL_F_SSL3_GET_NEW_SESSION_TICKET,ERR_R_MALLOC_FAILURE); goto err; } memcpy(s->session->tlsext_tick, p, ticklen); @@ -1712,6 +1733,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 95e893737c..06e454c96b 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -1936,6 +1936,44 @@ long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg) s->tlsext_debug_arg=parg; ret = 1; break; + + 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; @@ -2156,6 +2194,12 @@ long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) } return 1; } + + case SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB_ARG: + ctx->tlsext_status_arg=parg; + return 1; + break; + #endif /* !OPENSSL_NO_TLSEXT */ /* A Thawte special :-) */ case SSL_CTRL_EXTRA_CHAIN_CERT: @@ -2206,6 +2250,11 @@ long ssl3_ctx_callback_ctrl(SSL_CTX *ctx, int cmd, void (*fp)(void)) case SSL_CTRL_SET_TLSEXT_SERVERNAME_CB: ctx->tlsext_servername_callback=(int (*)(SSL *,int *,void *))fp; break; + + 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 6dba5c1977..fadb83ae61 100644 --- a/ssl/s3_srvr.c +++ b/ssl/s3_srvr.c @@ -306,10 +306,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; @@ -512,6 +525,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: @@ -2740,4 +2761,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 52caf88b16..10f6d81c2a 100644 --- a/ssl/ssl.h +++ b/ssl/ssl.h @@ -767,6 +767,11 @@ struct ssl_ctx_st unsigned char tlsext_tick_key_name[16]; 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; #endif }; @@ -1003,6 +1008,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; SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */ @@ -1158,6 +1175,7 @@ size_t SSL_get_peer_finished(const SSL *s, void *buf, size_t count); #define SSL_AD_UNSUPPORTED_EXTENSION TLS1_AD_UNSUPPORTED_EXTENSION #define SSL_AD_CERTIFICATE_UNOBTAINABLE TLS1_AD_CERTIFICATE_UNOBTAINABLE #define SSL_AD_UNRECOGNIZED_NAME TLS1_AD_UNRECOGNIZED_NAME +#define SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE #define SSL_ERROR_NONE 0 #define SSL_ERROR_SSL 1 @@ -1225,6 +1243,16 @@ size_t SSL_get_peer_finished(const SSL *s, void *buf, size_t count); #define SSL_CTRL_SET_TLSEXT_DEBUG_ARG 57 #define SSL_CTRL_GET_TLSEXT_TICKET_KEYS 58 #define SSL_CTRL_SET_TLSEXT_TICKET_KEYS 59 + +#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) \ @@ -1670,6 +1698,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 @@ -1854,6 +1883,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 316 #define SSL_R_INVALID_TICKET_KEYS_LENGTH 275 #define SSL_R_INVALID_TRUST 279 #define SSL_R_KEY_ARG_TOO_LONG 284 @@ -2010,6 +2040,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 f484df3f65..4b1e2e9834 100644 --- a/ssl/ssl3.h +++ b/ssl/ssl3.h @@ -483,6 +483,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 */ @@ -526,6 +528,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 @@ -538,6 +542,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 62f06d5f10..50779c1632 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -141,6 +141,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"}, @@ -328,6 +329,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"}, @@ -484,6 +486,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 73707aab9b..065411aea8 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -126,6 +126,7 @@ #include #include #include +#include #ifndef OPENSSL_NO_DH #include #endif @@ -311,6 +312,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 @@ -501,6 +508,13 @@ void SSL_free(SSL *s) if (s->ctx) SSL_CTX_free(s->ctx); #ifndef OPENSSL_NO_TLSEXT if (s->initial_ctx) SSL_CTX_free(s->initial_ctx); + 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) sk_X509_NAME_pop_free(s->client_CA,X509_NAME_free); @@ -1494,6 +1508,9 @@ SSL_CTX *SSL_CTX_new(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 return(ret); diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index e9fba49c53..de94c0d0c7 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -777,6 +777,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); @@ -870,6 +871,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_lib.c b/ssl/t1_lib.c index fabc634d68..1bd67b40b1 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -60,6 +60,7 @@ #include #include #include +#include #include "ssl_locl.h" const char tls1_version_str[]="TLSv1" OPENSSL_VERSION_PTEXT; @@ -190,6 +191,54 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha } } + 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; @@ -220,7 +269,14 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha s2n(TLSEXT_TYPE_session_ticket,ret); 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); + } + if ((extdatalen = ret-p-2)== 0) return p; @@ -235,6 +291,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; @@ -349,6 +406,106 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in } } + 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; @@ -403,6 +560,19 @@ int ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in } s->tlsext_ticket_expected = 1; } + 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; } @@ -448,6 +618,37 @@ 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; + err: switch (ret) { case SSL_TLSEXT_ERR_ALERT_FATAL: @@ -475,6 +676,35 @@ int ssl_check_serverhello_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 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; + ret = SSL_TLSEXT_ERR_ALERT_FATAL; + } + if (r < 0) + { + al = SSL_AD_INTERNAL_ERROR; + ret = SSL_TLSEXT_ERR_ALERT_FATAL; + } + } + switch (ret) { case SSL_TLSEXT_ERR_ALERT_FATAL: diff --git a/ssl/tls1.h b/ssl/tls1.h index 00399f9886..7c3b6a8a85 100644 --- a/ssl/tls1.h +++ b/ssl/tls1.h @@ -117,6 +117,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 #ifndef OPENSSL_NO_TLSEXT @@ -134,12 +136,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) \ @@ -149,6 +172,13 @@ SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG,0, (void *)arg) SSL_CTX_ctrl((ctx),SSL_CTRL_GET_TLXEXT_TICKET_KEYS,(keylen),(keys)) #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) + #endif /* Additional TLS ciphersuites from draft-ietf-tls-56-bit-ciphersuites-00.txt diff --git a/util/libeay.num b/util/libeay.num index f6519a516e..5f3d6b589c 100755 --- a/util/libeay.num +++ b/util/libeay.num @@ -3521,3 +3521,11 @@ SEED_ecb_encrypt 3915 EXIST::FUNCTION:SEED EVP_seed_ecb 3916 EXIST::FUNCTION:SEED SEED_set_key 3917 EXIST::FUNCTION:SEED EVP_seed_cfb128 3918 EXIST::FUNCTION:SEED +X509_EXTENSIONS_it 3919 EXIST:!EXPORT_VAR_AS_FUNCTION:VARIABLE: +X509_EXTENSIONS_it 3919 EXIST:EXPORT_VAR_AS_FUNCTION:FUNCTION: +X509_get1_ocsp 3920 EXIST::FUNCTION: +OCSP_REQ_CTX_free 3921 EXIST::FUNCTION: +i2d_X509_EXTENSIONS 3922 EXIST::FUNCTION: +OCSP_sendreq_nbio 3923 EXIST::FUNCTION: +OCSP_sendreq_new 3924 EXIST::FUNCTION: +d2i_X509_EXTENSIONS 3925 EXIST::FUNCTION: -- 2.25.1