From 5c8d41be859b95f34d044d0e06b1b0d7187cc5b2 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Thu, 29 Nov 2012 01:15:09 +0000 Subject: [PATCH] Generalise OCSP I/O functions to support dowloading of other ASN1 structures using HTTP. Add wrapper function to handle CRL download. --- CHANGES | 5 ++ crypto/ocsp/ocsp.h | 9 +++ crypto/ocsp/ocsp_ht.c | 165 +++++++++++++++++++++++++++++++----------- crypto/x509/x509.h | 1 + crypto/x509/x_all.c | 8 ++ 5 files changed, 146 insertions(+), 42 deletions(-) diff --git a/CHANGES b/CHANGES index 746f50206d..6f60d42394 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,11 @@ Changes between 1.0.1 and 1.0.2 [xx XXX xxxx] + *) Extend OCSP I/O functions so they can be used for simple general purpose + HTTP as well as OCSP. New wrapper function which can be used to download + CRLs using the OCSP API. + [Steve Henson] + *) Delegate command line handling in s_client/s_server to SSL_CONF APIs. [Steve Henson] diff --git a/crypto/ocsp/ocsp.h b/crypto/ocsp/ocsp.h index 31e45744ba..2ad7cf2d26 100644 --- a/crypto/ocsp/ocsp.h +++ b/crypto/ocsp/ocsp.h @@ -404,8 +404,17 @@ OCSP_CERTID *OCSP_CERTID_dup(OCSP_CERTID *id); 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_REQ_CTX_nbio(OCSP_REQ_CTX *rctx); int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx); +OCSP_REQ_CTX *OCSP_REQ_CTX_new(BIO *io, int maxline); void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx); +void OCSP_set_max_response_length(OCSP_REQ_CTX *rctx, unsigned long len); +int OCSP_REQ_CTX_i2d(OCSP_REQ_CTX *rctx, const ASN1_ITEM *it, ASN1_VALUE *val); +int OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx, + ASN1_VALUE **pval, const ASN1_ITEM *it); +BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx); +int OCSP_REQ_CTX_i2d(OCSP_REQ_CTX *rctx, const ASN1_ITEM *it, ASN1_VALUE *val); +int OCSP_REQ_CTX_http(OCSP_REQ_CTX *rctx, const char *op, const char *path); int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req); int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx, const char *name, const char *value); diff --git a/crypto/ocsp/ocsp_ht.c b/crypto/ocsp/ocsp_ht.c index af5fc16691..de34a91c53 100644 --- a/crypto/ocsp/ocsp_ht.c +++ b/crypto/ocsp/ocsp_ht.c @@ -80,9 +80,10 @@ struct ocsp_req_ctx_st { BIO *io; /* BIO to perform I/O with */ BIO *mem; /* Memory BIO response is built into */ unsigned long asn1_len; /* ASN1 length of response */ + unsigned long max_resp_len; /* Maximum length of response */ }; -#define OCSP_MAX_REQUEST_LENGTH (100 * 1024) +#define OCSP_MAX_RESP_LENGTH (100 * 1024) #define OCSP_MAX_LINE_LEN 4096; /* OCSP states */ @@ -99,16 +100,44 @@ struct ocsp_req_ctx_st { #define OHS_ASN1_HEADER 3 /* OCSP content octets being read */ #define OHS_ASN1_CONTENT 4 +/* First call: ready to start I/O */ +#define OHS_ASN1_WRITE_INIT (5 | OHS_NOREAD) /* 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) +/* Headers set, no final \r\n included */ +#define OHS_HTTP_HEADER (9 | OHS_NOREAD) static int parse_http_line1(char *line); +OCSP_REQ_CTX *OCSP_REQ_CTX_new(BIO *io, int maxline) + { + OCSP_REQ_CTX *rctx; + rctx = OPENSSL_malloc(sizeof(OCSP_REQ_CTX)); + if (!rctx) + return NULL; + rctx->state = OHS_ERROR; + rctx->max_resp_len = OCSP_MAX_RESP_LENGTH; + rctx->mem = BIO_new(BIO_s_mem()); + rctx->io = io; + rctx->asn1_len = 0; + if (maxline > 0) + rctx->iobuflen = maxline; + else + rctx->iobuflen = OCSP_MAX_LINE_LEN; + rctx->iobuf = OPENSSL_malloc(rctx->iobuflen); + if (!rctx->iobuf || !rctx->mem) + { + OCSP_REQ_CTX_free(rctx); + return NULL; + } + return rctx; + } + void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx) { if (rctx->mem) @@ -118,20 +147,72 @@ void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx) OPENSSL_free(rctx); } -int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req) +BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx) + { + return rctx->mem; + } + +void OCSP_set_max_response_length(OCSP_REQ_CTX *rctx, unsigned long len) + { + if (len == 0) + rctx->max_resp_len = OCSP_MAX_RESP_LENGTH; + else + rctx->max_resp_len = len; + } + +int OCSP_REQ_CTX_i2d(OCSP_REQ_CTX *rctx, const ASN1_ITEM *it, ASN1_VALUE *val) { static const char req_hdr[] = - "Content-Type: application/ocsp-request\r\n" - "Content-Length: %d\r\n\r\n"; - if (BIO_printf(rctx->mem, req_hdr, i2d_OCSP_REQUEST(req, NULL)) <= 0) + "Content-Type: application/ocsp-request\r\n" + "Content-Length: %d\r\n\r\n"; + int reqlen = ASN1_item_i2d(val, NULL, it); + if (BIO_printf(rctx->mem, req_hdr, reqlen) <= 0) return 0; - if (i2d_OCSP_REQUEST_bio(rctx->mem, req) <= 0) + if (ASN1_item_i2d_bio(it, rctx->mem, val) <= 0) return 0; - rctx->state = OHS_ASN1_WRITE; - rctx->asn1_len = BIO_get_mem_data(rctx->mem, NULL); + rctx->state = OHS_ASN1_WRITE_INIT; return 1; } +int OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx, + ASN1_VALUE **pval, const ASN1_ITEM *it) + { + int rv, len; + const unsigned char *p; + + rv = OCSP_REQ_CTX_nbio(rctx); + if (rv != 1) + return rv; + + len = BIO_get_mem_data(rctx->mem, &p); + *pval = ASN1_item_d2i(NULL, &p, len, it); + if (*pval == NULL) + { + rctx->state = OHS_ERROR; + return 0; + } + return 1; + } + +int OCSP_REQ_CTX_http(OCSP_REQ_CTX *rctx, const char *op, const char *path) + { + static const char http_hdr[] = "%s %s HTTP/1.0\r\n"; + + if (!path) + path = "/"; + + if (BIO_printf(rctx->mem, http_hdr, op, path) <= 0) + return 0; + rctx->state = OHS_HTTP_HEADER; + return 1; + } + +int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req) + { + return OCSP_REQ_CTX_i2d(rctx, ASN1_ITEM_rptr(OCSP_REQUEST), + (ASN1_VALUE *)req); + } + int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx, const char *name, const char *value) { @@ -148,37 +229,30 @@ int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx, } if (BIO_write(rctx->mem, "\r\n", 2) != 2) return 0; + rctx->state = OHS_HTTP_HEADER; return 1; } OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, char *path, OCSP_REQUEST *req, int maxline) { - static const char post_hdr[] = "POST %s HTTP/1.0\r\n"; - OCSP_REQ_CTX *rctx; - rctx = OPENSSL_malloc(sizeof(OCSP_REQ_CTX)); - rctx->state = OHS_ERROR; - rctx->mem = BIO_new(BIO_s_mem()); - rctx->io = io; - rctx->asn1_len = 0; - if (maxline > 0) - rctx->iobuflen = maxline; - else - rctx->iobuflen = OCSP_MAX_LINE_LEN; - rctx->iobuf = OPENSSL_malloc(rctx->iobuflen); - if (!rctx->iobuf) - return 0; - if (!path) - path = "/"; + OCSP_REQ_CTX *rctx = NULL; + rctx = OCSP_REQ_CTX_new(io, maxline); + if (!rctx) + return NULL; - if (BIO_printf(rctx->mem, post_hdr, path) <= 0) - return 0; + if (!OCSP_REQ_CTX_http(rctx, "POST", path)) + goto err; if (req && !OCSP_REQ_CTX_set1_req(rctx, req)) - return 0; + goto err; return rctx; + + err: + OCSP_REQ_CTX_free(rctx); + return NULL; } /* Parse the HTTP response. This will look like this: @@ -260,7 +334,7 @@ static int parse_http_line1(char *line) } -int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx) +int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx) { int i, n; const unsigned char *p; @@ -284,6 +358,18 @@ int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx) switch(rctx->state) { + case OHS_HTTP_HEADER: + /* Last operation was adding headers: need a final \r\n */ + if (BIO_write(rctx->mem, "\r\n", 2) != 2) + { + rctx->state = OHS_ERROR; + return 0; + } + rctx->state = OHS_ASN1_WRITE_INIT; + + case OHS_ASN1_WRITE_INIT: + rctx->asn1_len = BIO_get_mem_data(rctx->mem, NULL); + rctx->state = OHS_ASN1_WRITE; case OHS_ASN1_WRITE: n = BIO_get_mem_data(rctx->mem, &p); @@ -435,7 +521,7 @@ int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx) rctx->asn1_len |= *p++; } - if (rctx->asn1_len > OCSP_MAX_REQUEST_LENGTH) + if (rctx->asn1_len > rctx->max_resp_len) { rctx->state = OHS_ERROR; return 0; @@ -451,20 +537,12 @@ int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx) /* Fall thru */ case OHS_ASN1_CONTENT: - n = BIO_get_mem_data(rctx->mem, &p); + n = BIO_get_mem_data(rctx->mem, NULL); 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; + rctx->state = OHS_DONE; + return 1; break; @@ -473,11 +551,14 @@ int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx) } - - return 0; + } +int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx) + { + return OCSP_REQ_CTX_nbio_d2i(rctx, + (ASN1_VALUE **)presp, ASN1_ITEM_rptr(OCSP_RESPONSE)); } /* Blocking OCSP request handler: now a special case of non-blocking I/O */ diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h index e86a7fd744..78c18666af 100644 --- a/crypto/x509/x509.h +++ b/crypto/x509/x509.h @@ -666,6 +666,7 @@ int X509_REQ_sign(X509_REQ *x, EVP_PKEY *pkey, const EVP_MD *md); int X509_REQ_sign_ctx(X509_REQ *x, EVP_MD_CTX *ctx); int X509_CRL_sign(X509_CRL *x, EVP_PKEY *pkey, const EVP_MD *md); int X509_CRL_sign_ctx(X509_CRL *x, EVP_MD_CTX *ctx); +int X509_CRL_http_nbio(OCSP_REQ_CTX *rctx, X509_CRL **pcrl); int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *x, EVP_PKEY *pkey, const EVP_MD *md); int X509_pubkey_digest(const X509 *data,const EVP_MD *type, diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c index b94aeeb873..e62cd31394 100644 --- a/crypto/x509/x_all.c +++ b/crypto/x509/x_all.c @@ -63,6 +63,7 @@ #include #include #include +#include #ifndef OPENSSL_NO_RSA #include #endif @@ -127,6 +128,13 @@ int X509_CRL_sign_ctx(X509_CRL *x, EVP_MD_CTX *ctx) x->crl->sig_alg, x->sig_alg, x->signature, x->crl, ctx); } +int X509_CRL_http_nbio(OCSP_REQ_CTX *rctx, X509_CRL **pcrl) + { + return OCSP_REQ_CTX_nbio_d2i(rctx, + (ASN1_VALUE **)pcrl, ASN1_ITEM_rptr(X509_CRL)); + } + + int NETSCAPE_SPKI_sign(NETSCAPE_SPKI *x, EVP_PKEY *pkey, const EVP_MD *md) { return(ASN1_item_sign(ASN1_ITEM_rptr(NETSCAPE_SPKAC), x->sig_algor,NULL, -- 2.25.1