diff --git a/CHANGES.md b/CHANGES.md index 80a7bc7075..e4e33e4e88 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -358,18 +358,20 @@ OpenSSL 3.0 * Deprecated the type OCSP_REQ_CTX and the functions OCSP_REQ_CTX_new(), OCSP_REQ_CTX_free(), OCSP_REQ_CTX_http(), OCSP_REQ_CTX_add1_header(), OCSP_REQ_CTX_i2d() and its special form OCSP_REQ_CTX_set1_req(), - OCSP_REQ_CTX_nbio(), OCSP_REQ_CTX_nbio_d2i(), + OCSP_REQ_CTX_nbio(), + OCSP_REQ_CTX_nbio_d2i() and its special form OCSP_sendreq_nbio(), OCSP_REQ_CTX_get0_mem_bio() and OCSP_set_max_response_length(). These were used to collect all necessary data to form a HTTP request, and to perform the HTTP transfer with that request. With OpenSSL 3.0, the type is OSSL_HTTP_REQ_CTX, and the deprecated functions are replaced with OSSL_HTTP_REQ_CTX_new(), OSSL_HTTP_REQ_CTX_free(), OSSL_HTTP_REQ_CTX_set_request_line(), OSSL_HTTP_REQ_CTX_add1_header(), - OSSL_HTTP_REQ_CTX_i2d(), OSSL_HTTP_REQ_CTX_nbio(), - OSSL_HTTP_REQ_CTX_sendreq_d2i(), OSSL_HTTP_REQ_CTX_get0_mem_bio() and + OSSL_HTTP_REQ_CTX_set1_req(), + OSSL_HTTP_REQ_CTX_nbio(), OSSL_HTTP_REQ_CTX_nbio_d2i(), + OSSL_HTTP_REQ_CTX_get0_mem_bio(), and OSSL_HTTP_REQ_CTX_set_max_response_length(). - *Rich Salz and Richard Levitte* + *Rich Salz, Richard Levitte, and David von Oheimb* * Deprecated `X509_http_nbio()` and `X509_CRL_http_nbio()`, which are superseded by `X509_load_http()` and `X509_CRL_load_http()`. @@ -812,8 +814,12 @@ OpenSSL 3.0 *David von Oheimb, Martin Peylo* * Generalized the HTTP client code from `crypto/ocsp/` into `crpyto/http/`. - The legacy OCSP-focused and only partly documented API is retained for - backward compatibility. See L etc. for details. + It supports arbitrary request and response content types, GET redirection, + TLS, connections via HTTP(S) proxies, connections and exchange via + user-defined BIOs (allowing implicit connections), persistent connections, + and timeout checks. See L etc. for details. + The legacy OCSP-focused (and only partly documented) API + is retained for backward compatibility, while most of it is deprecated. *David von Oheimb* diff --git a/apps/lib/apps.c b/apps/lib/apps.c index f0a9ffc93a..67e089bcd4 100644 --- a/apps/lib/apps.c +++ b/apps/lib/apps.c @@ -2479,6 +2479,7 @@ ASN1_VALUE *app_http_get_asn1(const char *url, const char *proxy, char *server; char *port; int use_ssl; + BIO *mem; ASN1_VALUE *resp = NULL; if (url == NULL || it == NULL) { @@ -2500,10 +2501,13 @@ ASN1_VALUE *app_http_get_asn1(const char *url, const char *proxy, info.use_proxy = proxy != NULL; info.timeout = timeout; info.ssl_ctx = ssl_ctx; - resp = OSSL_HTTP_get_asn1(url, proxy, no_proxy, - NULL, NULL, app_http_tls_cb, &info, - headers, 0 /* maxline */, 0 /* max_resp_len */, - timeout, expected_content_type, it); + mem = OSSL_HTTP_get(url, proxy, no_proxy, NULL /* bio */, NULL /* rbio */, + app_http_tls_cb, &info, 0 /* buf_size */, headers, + expected_content_type, 1 /* expect_asn1 */, + HTTP_DEFAULT_MAX_RESP_LEN, timeout); + resp = ASN1_item_d2i_bio(it, mem, NULL); + BIO_free(mem); + end: OPENSSL_free(server); OPENSSL_free(port); @@ -2520,18 +2524,27 @@ ASN1_VALUE *app_http_post_asn1(const char *host, const char *port, long timeout, const ASN1_ITEM *rsp_it) { APP_HTTP_TLS_INFO info; + BIO *rsp, *req_mem = ASN1_item_i2d_mem_bio(req_it, req); + ASN1_VALUE *res; + if (req_mem == NULL) + return NULL; info.server = host; info.port = port; info.use_proxy = proxy != NULL; info.timeout = timeout; info.ssl_ctx = ssl_ctx; - return OSSL_HTTP_post_asn1(host, port, path, ssl_ctx != NULL, - proxy, no_proxy, - NULL, NULL, app_http_tls_cb, &info, - headers, content_type, req, req_it, - 0 /* maxline */, - 0 /* max_resp_len */, timeout, NULL, rsp_it); + rsp = OSSL_HTTP_transfer(NULL, host, port, path, ssl_ctx != NULL, + proxy, no_proxy, NULL /* bio */, NULL /* rbio */, + app_http_tls_cb, &info, + 0 /* buf_size */, headers, content_type, req_mem, + NULL /* expected_ct */, 1 /* expect_asn1 */, + HTTP_DEFAULT_MAX_RESP_LEN, timeout, + 0 /* keep_alive */); + BIO_free(req_mem); + res = ASN1_item_d2i_bio(rsp_it, rsp, NULL); + BIO_free(rsp); + return res; } #endif diff --git a/crypto/cmp/cmp_http.c b/crypto/cmp/cmp_http.c index 215c47c7c5..a358622feb 100644 --- a/crypto/cmp/cmp_http.c +++ b/crypto/cmp/cmp_http.c @@ -37,9 +37,11 @@ OSSL_CMP_MSG *OSSL_CMP_MSG_http_perform(OSSL_CMP_CTX *ctx, { char server_port[32] = { '\0' }; STACK_OF(CONF_VALUE) *headers = NULL; - const char *const content_type_pkix = "application/pkixcmp"; + const char content_type_pkix[] = "application/pkixcmp"; int tls_used; - OSSL_CMP_MSG *res; + const ASN1_ITEM *it = ASN1_ITEM_rptr(OSSL_CMP_MSG); + BIO *req_mem, *rsp; + OSSL_CMP_MSG *res = NULL; if (ctx == NULL || req == NULL) { ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT); @@ -48,6 +50,8 @@ OSSL_CMP_MSG *OSSL_CMP_MSG_http_perform(OSSL_CMP_CTX *ctx, if (!X509V3_add_value("Pragma", "no-cache", &headers)) return NULL; + if ((req_mem = ASN1_item_i2d_mem_bio(it, (const ASN1_VALUE *)req)) == NULL) + goto err; if (ctx->serverPort != 0) BIO_snprintf(server_port, sizeof(server_port), "%d", ctx->serverPort); @@ -55,15 +59,21 @@ OSSL_CMP_MSG *OSSL_CMP_MSG_http_perform(OSSL_CMP_CTX *ctx, tls_used = OSSL_CMP_CTX_get_http_cb_arg(ctx) != NULL; ossl_cmp_log2(DEBUG, ctx, "connecting to CMP server %s%s", ctx->server, tls_used ? " using TLS" : ""); - res = (OSSL_CMP_MSG *) - OSSL_HTTP_post_asn1(ctx->server, server_port, ctx->serverPath, - tls_used, ctx->proxy, ctx->no_proxy, NULL, NULL, - ctx->http_cb, OSSL_CMP_CTX_get_http_cb_arg(ctx), - headers, content_type_pkix, (const ASN1_VALUE *)req, - ASN1_ITEM_rptr(OSSL_CMP_MSG), - 0, 0, ctx->msg_timeout, content_type_pkix, - ASN1_ITEM_rptr(OSSL_CMP_MSG)); + rsp = OSSL_HTTP_transfer(NULL, ctx->server, server_port, + ctx->serverPath, tls_used, + ctx->proxy, ctx->no_proxy, + NULL /* bio */, NULL /* rbio */, + ctx->http_cb, OSSL_CMP_CTX_get_http_cb_arg(ctx), + 0 /* buf_size */, headers, + content_type_pkix, req_mem, + content_type_pkix, 1 /* expect_asn1 */, + HTTP_DEFAULT_MAX_RESP_LEN, + ctx->msg_timeout, 0 /* keep_alive */); + BIO_free(req_mem); + res = (OSSL_CMP_MSG *)ASN1_item_d2i_bio(it, rsp, NULL); + BIO_free(rsp); ossl_cmp_debug(ctx, "disconnected from CMP server"); + err: sk_CONF_VALUE_pop_free(headers, X509V3_conf_free); return res; } diff --git a/crypto/http/http_client.c b/crypto/http/http_client.c index bf2e3b54c7..c32b352137 100644 --- a/crypto/http/http_client.c +++ b/crypto/http/http_client.c @@ -48,12 +48,14 @@ struct ossl_http_req_ctx_st { BIO *rbio; /* BIO to read response from */ BIO *mem; /* Memory BIO response is built into */ int method_POST; /* HTTP method is "POST" (else "GET") */ - const char *expected_ct; /* expected Content-Type, or NULL */ + char *expected_ct; /* expected Content-Type, or NULL */ int expect_asn1; /* response must be ASN.1-encoded */ long len_to_send; /* number of bytes in request still to send */ unsigned long resp_len; /* length of response */ unsigned long max_resp_len; /* Maximum length of response */ - time_t max_time; /* Maximum end time of the transfer, or 0 */ + int keep_alive; /* Persistent conn. 0=no, 1=prefer, 2=require */ + time_t max_time; /* Maximum end time of current transfer, or 0 */ + time_t max_total_time; /* Maximum end time of total transfer, or 0 */ char *redirection_url; /* Location given with HTTP status 301/302 */ }; @@ -72,10 +74,7 @@ struct ossl_http_req_ctx_st { #define OHS_DONE (8 | OHS_NOREAD) /* Completed */ #define OHS_HTTP_HEADER (9 | OHS_NOREAD) /* Headers set, w/o final \r\n */ -OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, - int maxline, unsigned long max_resp_len, - int timeout, const char *expected_ct, - int expect_asn1) +OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int maxline) { OSSL_HTTP_REQ_CTX *rctx; @@ -95,11 +94,8 @@ OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, OPENSSL_free(rctx); return NULL; } - rctx->expected_ct = expected_ct; - rctx->expect_asn1 = expect_asn1; rctx->resp_len = 0; - OSSL_HTTP_REQ_CTX_set_max_response_length(rctx, max_resp_len); - rctx->max_time = timeout > 0 ? time(NULL) + timeout : 0; + rctx->max_resp_len = HTTP_DEFAULT_MAX_RESP_LEN; /* everything else is 0, e.g. rctx->len_to_send, or NULL, e.g. rctx->mem */ return rctx; } @@ -110,6 +106,7 @@ void OSSL_HTTP_REQ_CTX_free(OSSL_HTTP_REQ_CTX *rctx) return; BIO_free(rctx->mem); /* this may indirectly call ERR_clear_error() */ OPENSSL_free(rctx->readbuf); + OPENSSL_free(rctx->expected_ct); OPENSSL_free(rctx); } @@ -122,6 +119,15 @@ BIO *OSSL_HTTP_REQ_CTX_get0_mem_bio(const OSSL_HTTP_REQ_CTX *rctx) return rctx->mem; } +size_t OSSL_HTTP_REQ_CTX_get_resp_len(const OSSL_HTTP_REQ_CTX *rctx) +{ + if (rctx == NULL) { + ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + return rctx->resp_len; +} + void OSSL_HTTP_REQ_CTX_set_max_response_length(OSSL_HTTP_REQ_CTX *rctx, unsigned long len) { @@ -201,6 +207,36 @@ int OSSL_HTTP_REQ_CTX_add1_header(OSSL_HTTP_REQ_CTX *rctx, return 1; } +int OSSL_HTTP_REQ_CTX_set_expected(OSSL_HTTP_REQ_CTX *rctx, + const char *content_type, int asn1, + int timeout, int keep_alive) +{ + if (rctx == NULL) { + ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + if (keep_alive != 0 + && rctx->state != OHS_ERROR && rctx->state != OHS_HEADERS) { + /* Cannot anymore set keep-alive in request header */ + ERR_raise(ERR_LIB_HTTP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + + OPENSSL_free(rctx->expected_ct); + rctx->expected_ct = NULL; + if (content_type != NULL + && (rctx->expected_ct = OPENSSL_strdup(content_type)) == NULL) + return 0; + + rctx->expect_asn1 = asn1; + if (timeout >= 0) + rctx->max_time = timeout > 0 ? time(NULL) + timeout : 0; + else + rctx->max_time = rctx->max_total_time; + rctx->keep_alive = keep_alive; + return 1; +} + static int ossl_http_req_ctx_set_content(OSSL_HTTP_REQ_CTX *rctx, const char *content_type, BIO *req_mem) { @@ -228,26 +264,8 @@ static int ossl_http_req_ctx_set_content(OSSL_HTTP_REQ_CTX *rctx, && BIO_write(rctx->mem, req, req_len) == (int)req_len; } -BIO *ossl_http_asn1_item2bio(const ASN1_ITEM *it, const ASN1_VALUE *val) -{ - BIO *res; - - if (it == NULL || val == NULL) { - ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER); - return NULL; - } - - if ((res = BIO_new(BIO_s_mem())) == NULL) - return NULL; - if (ASN1_item_i2d_bio(it, res, val) <= 0) { - BIO_free(res); - res = NULL; - } - return res; -} - int OSSL_HTTP_REQ_CTX_set1_req(OSSL_HTTP_REQ_CTX *rctx, const char *content_type, - const ASN1_ITEM *it, ASN1_VALUE *req) + const ASN1_ITEM *it, const ASN1_VALUE *req) { BIO *mem; int res; @@ -257,7 +275,7 @@ int OSSL_HTTP_REQ_CTX_set1_req(OSSL_HTTP_REQ_CTX *rctx, const char *content_type return 0; } - res = (mem = ossl_http_asn1_item2bio(it, req)) != NULL + res = (mem = ASN1_item_i2d_mem_bio(it, req)) != NULL && ossl_http_req_ctx_set_content(rctx, content_type, mem); BIO_free(mem); return res; @@ -289,14 +307,13 @@ static int OSSL_HTTP_REQ_CTX_add1_headers(OSSL_HTTP_REQ_CTX *rctx, * If !use_http_proxy then the 'server' and 'port' parameters are ignored. * If req_mem == NULL then use GET and ignore content_type, else POST. */ -OSSL_HTTP_REQ_CTX +static OSSL_HTTP_REQ_CTX *ossl_http_req_ctx_new(BIO *wbio, BIO *rbio, int use_http_proxy, const char *server, const char *port, const char *path, const STACK_OF(CONF_VALUE) *headers, const char *content_type, BIO *req_mem, - int maxline, unsigned long max_resp_len, - int timeout, + int maxline, int timeout, const char *expected_ct, int expect_asn1) { OSSL_HTTP_REQ_CTX *rctx; @@ -307,14 +324,14 @@ OSSL_HTTP_REQ_CTX } /* remaining parameters are checked indirectly by the functions called */ - if ((rctx = OSSL_HTTP_REQ_CTX_new(wbio, rbio, maxline, max_resp_len, timeout, - expected_ct, expect_asn1)) + if ((rctx = OSSL_HTTP_REQ_CTX_new(wbio, rbio, maxline)) == NULL) return NULL; - if (OSSL_HTTP_REQ_CTX_set_request_line(rctx, req_mem != NULL, use_http_proxy ? server : NULL, port, path) + && OSSL_HTTP_REQ_CTX_set_expected(rctx, expected_ct, expect_asn1, + timeout, 0) && OSSL_HTTP_REQ_CTX_add1_headers(rctx, headers, server) && (req_mem == NULL || ossl_http_req_ctx_set_content(rctx, content_type, req_mem))) @@ -588,6 +605,7 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx) rctx->expected_ct, value); return 0; } + OPENSSL_free(rctx->expected_ct); rctx->expected_ct = NULL; /* content-type has been found */ } if (strcasecmp(key, "Content-Length") == 0) { @@ -688,6 +706,20 @@ int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx) } } +int OSSL_HTTP_REQ_CTX_nbio_d2i(OSSL_HTTP_REQ_CTX *rctx, + ASN1_VALUE **pval, const ASN1_ITEM *it) +{ + const unsigned char *p; + int rv; + + *pval = NULL; + if ((rv = OSSL_HTTP_REQ_CTX_nbio(rctx)) != 1) + return rv; + *pval = ASN1_item_d2i(NULL, &p, BIO_get_mem_data(rctx->mem, &p), it); + return *pval != NULL; + +} + #ifndef OPENSSL_NO_SOCK /* set up a new connection BIO, to HTTP server or to HTTP(S) proxy if given */ @@ -723,20 +755,12 @@ static BIO *HTTP_new_bio(const char *server /* optionally includes ":port" */, } #endif /* OPENSSL_NO_SOCK */ -static ASN1_VALUE *BIO_mem_d2i(BIO *mem, const ASN1_ITEM *it) +int OSSL_HTTP_is_alive(const OSSL_HTTP_REQ_CTX *rctx) { - const unsigned char *p; - ASN1_VALUE *resp; - - if (mem == NULL) - return NULL; - - if ((resp = ASN1_item_d2i(NULL, &p, BIO_get_mem_data(mem, &p), it)) == NULL) - ERR_raise(ERR_LIB_HTTP, HTTP_R_RESPONSE_PARSE_ERROR); - return resp; + return rctx != NULL && rctx->keep_alive != 0; } -static BIO *ossl_http_req_ctx_transfer(OSSL_HTTP_REQ_CTX *rctx) +BIO *OSSL_HTTP_REQ_CTX_exchange(OSSL_HTTP_REQ_CTX *rctx) { int rv; @@ -767,17 +791,6 @@ static BIO *ossl_http_req_ctx_transfer(OSSL_HTTP_REQ_CTX *rctx) return rctx->mem; } -/* Exchange ASN.1-encoded request and response via HTTP on (non-)blocking BIO */ -ASN1_VALUE *OSSL_HTTP_REQ_CTX_sendreq_d2i(OSSL_HTTP_REQ_CTX *rctx, - const ASN1_ITEM *it) -{ - if (rctx == NULL || it == NULL) { - ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER); - return NULL; - } - return BIO_mem_d2i(ossl_http_req_ctx_transfer(rctx), it); -} - static int update_timeout(int timeout, time_t start_time) { long elapsed_time; @@ -788,6 +801,15 @@ static int update_timeout(int timeout, time_t start_time) return timeout <= elapsed_time ? -1 : timeout - elapsed_time; } +OSSL_HTTP_REQ_CTX *OSSL_HTTP_open(const char *server, const char *port, + const char *proxy, const char *no_proxy, + int use_ssl, BIO *bio, BIO *rbio, + OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, + int buf_size, int overall_timeout) +{ + return NULL; /* TODO(3.0) expand */ +} + /*- * Exchange HTTP request and response with the given server. * If req_mem == NULL then use GET and ignore content_type, else POST. @@ -815,16 +837,31 @@ static int update_timeout(int timeout, time_t start_time) * The function should return NULL to indicate failure. * After disconnect the modified BIO will be deallocated using BIO_free_all(). */ -BIO *OSSL_HTTP_transfer(const char *server, const char *port, const char *path, +int OSSL_HTTP_set_request(OSSL_HTTP_REQ_CTX *rctx, const char *path, + const STACK_OF(CONF_VALUE) *headers, + const char *content_type, BIO *req, + const char *expected_content_type, int expect_asn1, + size_t max_resp_len, int timeout, int keep_alive) +{ + return 0; /* TODO(3.0) expand */ +} + +BIO *OSSL_HTTP_exchange(OSSL_HTTP_REQ_CTX *rctx, char **redirection_url) +{ + return NULL; /* TODO(3.0) expand */ +} + +BIO *OSSL_HTTP_transfer(OSSL_HTTP_REQ_CTX **prctx, + const char *server, const char *port, const char *path, int use_ssl, const char *proxy, const char *no_proxy, BIO *bio, BIO *rbio, OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, - const STACK_OF(CONF_VALUE) *headers, + int maxline, const STACK_OF(CONF_VALUE) *headers, const char *content_type, BIO *req_mem, - int maxline, unsigned long max_resp_len, int timeout, const char *expected_ct, int expect_asn1, - char **redirection_url) + size_t max_resp_len, int timeout, int keep_alive) { + char **redirection_url = (char **)prctx; /* TODO(3.0) fix when API approved */ time_t start_time = timeout > 0 ? time(NULL) : 0; BIO *cbio; /* = bio if present, used as connection BIO if rbio is NULL */ OSSL_HTTP_REQ_CTX *rctx; @@ -892,12 +929,12 @@ BIO *OSSL_HTTP_transfer(const char *server, const char *port, const char *path, rctx = ossl_http_req_ctx_new(cbio, rbio != NULL ? rbio : cbio, !use_ssl && proxy != NULL, server, port, path, headers, content_type, req_mem, maxline, - max_resp_len, update_timeout(timeout, start_time), + update_timeout(timeout, start_time), expected_ct, expect_asn1); if (rctx == NULL) goto end; - resp = ossl_http_req_ctx_transfer(rctx); + resp = OSSL_HTTP_REQ_CTX_exchange(rctx); if (resp == NULL) { if (rctx->redirection_url != NULL) { if (redirection_url == NULL) @@ -981,12 +1018,12 @@ static int redirection_ok(int n_redir, const char *old_url, const char *new_url) BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy, BIO *bio, BIO *rbio, OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, - const STACK_OF(CONF_VALUE) *headers, - int maxline, unsigned long max_resp_len, int timeout, - const char *expected_ct, int expect_asn1) + int maxline, const STACK_OF(CONF_VALUE) *headers, + const char *expected_ct, int expect_asn1, + unsigned long max_resp_len, int timeout) { time_t start_time = timeout > 0 ? time(NULL) : 0; - char *current_url, *redirection_url; + char *current_url, *redirection_url = NULL; int n_redirs = 0; char *host; char *port; @@ -1007,13 +1044,13 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy, break; new_rpath: - resp = OSSL_HTTP_transfer(host, port, path, use_ssl, proxy, no_proxy, + resp = OSSL_HTTP_transfer((OSSL_HTTP_REQ_CTX **)&redirection_url, /* TODO(3.0) fix when API approved */ + host, port, path, use_ssl, proxy, no_proxy, bio, rbio, - bio_update_fn, arg, headers, NULL, NULL, - maxline, max_resp_len, - update_timeout(timeout, start_time), + bio_update_fn, arg, maxline, headers, NULL, NULL, expected_ct, expect_asn1, - &redirection_url); + max_resp_len, + update_timeout(timeout, start_time), 0); OPENSSL_free(path); if (resp == NULL && redirection_url != NULL) { if (redirection_ok(++n_redirs, current_url, redirection_url)) { @@ -1038,65 +1075,9 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy, return resp; } -/* Get ASN.1-encoded data via HTTP from server at given URL */ -ASN1_VALUE *OSSL_HTTP_get_asn1(const char *url, - const char *proxy, const char *no_proxy, - BIO *bio, BIO *rbio, - OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, - const STACK_OF(CONF_VALUE) *headers, - int maxline, unsigned long max_resp_len, - int timeout, const char *expected_ct, - const ASN1_ITEM *rsp_it) +int OSSL_HTTP_close(OSSL_HTTP_REQ_CTX *rctx, int ok) { - BIO *mem; - ASN1_VALUE *resp = NULL; - - if (url == NULL || rsp_it == NULL) { - ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER); - return NULL; - } - mem = OSSL_HTTP_get(url, proxy, no_proxy, bio, rbio, bio_update_fn, - arg, headers, maxline, max_resp_len, timeout, - expected_ct, 1 /* expect_asn1 */); - resp = BIO_mem_d2i(mem /* may be NULL */, rsp_it); - BIO_free(mem); - return resp; -} - -/* Post ASN.1-encoded request via HTTP to server return ASN.1 response */ -ASN1_VALUE *OSSL_HTTP_post_asn1(const char *server, const char *port, - const char *path, int use_ssl, - const char *proxy, const char *no_proxy, - BIO *bio, BIO *rbio, - OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, - const STACK_OF(CONF_VALUE) *headers, - const char *content_type, - const ASN1_VALUE *req, const ASN1_ITEM *req_it, - int maxline, unsigned long max_resp_len, - int timeout, const char *expected_ct, - const ASN1_ITEM *rsp_it) -{ - BIO *req_mem; - BIO *res_mem; - ASN1_VALUE *resp = NULL; - - if (req == NULL) { - ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER); - return NULL; - } - /* remaining parameters are checked indirectly */ - - req_mem = ossl_http_asn1_item2bio(req_it, req); - res_mem = OSSL_HTTP_transfer(server, port, path, use_ssl, proxy, no_proxy, - bio, rbio, - bio_update_fn, arg, headers, content_type, - req_mem /* may be NULL */, maxline, - max_resp_len, timeout, - expected_ct, 1 /* expect_asn1 */, NULL); - BIO_free(req_mem); - resp = BIO_mem_d2i(res_mem /* may be NULL */, rsp_it); - BIO_free(res_mem); - return resp; + return 0; /* TODO(3.0) expand */ } /* BASE64 encoder used for encoding basic proxy authentication credentials */ diff --git a/crypto/http/http_local.h b/crypto/http/http_local.h index 3164f62a77..16f7f7c8a5 100644 --- a/crypto/http/http_local.h +++ b/crypto/http/http_local.h @@ -11,21 +11,6 @@ #ifndef OSSL_CRYPTO_HTTP_LOCAL_H # define OSSL_CRYPTO_HTTP_LOCAL_H -# include - -BIO *ossl_http_asn1_item2bio(const ASN1_ITEM *it, const ASN1_VALUE *val); - -OSSL_HTTP_REQ_CTX -*ossl_http_req_ctx_new(BIO *wbio, BIO *rbio, int use_http_proxy, - const char *server, const char *port, - const char *path, - const STACK_OF(CONF_VALUE) *headers, - const char *content_type, BIO *req_mem, - int maxline, unsigned long max_resp_len, - int timeout, - const char *expected_content_type, - int expect_asn1); - int ossl_http_use_proxy(const char *no_proxy, const char *server); const char *ossl_http_adapt_proxy(const char *proxy, const char *no_proxy, const char *server, int use_ssl); diff --git a/crypto/ocsp/ocsp_http.c b/crypto/ocsp/ocsp_http.c index 7a3c19c860..8cf816e53f 100644 --- a/crypto/ocsp/ocsp_http.c +++ b/crypto/ocsp/ocsp_http.c @@ -16,17 +16,18 @@ OSSL_HTTP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, const OCSP_REQUEST *req, int maxline) { - OSSL_HTTP_REQ_CTX *rctx = NULL; + OSSL_HTTP_REQ_CTX *rctx = OSSL_HTTP_REQ_CTX_new(io, io, maxline); - if ((rctx = OSSL_HTTP_REQ_CTX_new(io, io, - maxline, 0 /* default max_resp_len */, - 0 /* no timeout, blocking indefinitely */, - NULL, 1 /* expect_asn1 */)) == NULL) + if (rctx == NULL) return NULL; if (!OSSL_HTTP_REQ_CTX_set_request_line(rctx, 1 /* POST */, NULL, NULL, path)) goto err; + if (!OSSL_HTTP_REQ_CTX_set_expected(rctx, + NULL /* content_type */, 1 /* asn1 */, + 0 /* timeout */, 0 /* keep_alive */)) + goto err; if (req != NULL && !OSSL_HTTP_REQ_CTX_set1_req(rctx, "application/ocsp-request", ASN1_ITEM_rptr(OCSP_REQUEST), @@ -40,23 +41,19 @@ OSSL_HTTP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, return NULL; } -int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OSSL_HTTP_REQ_CTX *rctx) -{ - *presp = (OCSP_RESPONSE *) - OSSL_HTTP_REQ_CTX_sendreq_d2i(rctx, ASN1_ITEM_rptr(OCSP_RESPONSE)); - return *presp != NULL; -} - OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req) { OCSP_RESPONSE *resp = NULL; OSSL_HTTP_REQ_CTX *ctx; + BIO *mem; ctx = OCSP_sendreq_new(b, path, req, -1 /* default max resp line length */); if (ctx == NULL) return NULL; - - OCSP_sendreq_nbio(&resp, ctx); + mem = OSSL_HTTP_REQ_CTX_exchange(ctx); + resp = (OCSP_RESPONSE *) + ASN1_item_d2i_bio(ASN1_ITEM_rptr(OCSP_RESPONSE), mem, NULL); + BIO_free(mem); /* this indirectly calls ERR_clear_error(): */ OSSL_HTTP_REQ_CTX_free(ctx); diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c index 9733597d37..1bd47ce654 100644 --- a/crypto/x509/x_all.c +++ b/crypto/x509/x_all.c @@ -75,11 +75,15 @@ int X509_sign_ctx(X509 *x, EVP_MD_CTX *ctx) static ASN1_VALUE *simple_get_asn1(const char *url, BIO *bio, BIO *rbio, int timeout, const ASN1_ITEM *it) { - return OSSL_HTTP_get_asn1(url, NULL, NULL /* no proxy used */, bio, - rbio, NULL /* no callback for SSL/TLS */, NULL, - NULL /* headers */, 1024 /* maxline */, - 0 /* max_resp_len */, timeout, - NULL /* expected_content_type */, it); + BIO *mem = OSSL_HTTP_get(url, NULL /* proxy */, NULL /* no_proxy */, + bio, rbio, NULL /* cb */ , NULL /* arg */, + 1024 /* buf_size */, NULL /* headers */, + NULL /* expected_ct */, 1 /* expect_asn1 */, + HTTP_DEFAULT_MAX_RESP_LEN, timeout); + ASN1_VALUE *res = ASN1_item_d2i_bio(it, mem, NULL); + + BIO_free(mem); + return res; } X509 *X509_load_http(const char *url, BIO *bio, BIO *rbio, int timeout) diff --git a/doc/man1/openssl-cmp.pod.in b/doc/man1/openssl-cmp.pod.in index 28ea4ee6a5..49105ca315 100644 --- a/doc/man1/openssl-cmp.pod.in +++ b/doc/man1/openssl-cmp.pod.in @@ -52,6 +52,7 @@ Message transfer options: [B<-proxy> I<[http[s]://][userinfo@]host[:port][/path][?query][#fragment]>] [B<-no_proxy> I] [B<-recipient> I] +[B<-keep_alive> I] [B<-msg_timeout> I] [B<-total_timeout> I] @@ -488,11 +489,20 @@ as far as any of those is present, else the NULL-DN as last resort. The argument must be formatted as I. For details see the description of the B<-subject> option. +=item B<-keep_alive> I + +If the given value is 0 then HTTP connections are not kept open +after receiving a response, which is the default behavior for HTTP 1.0. +If the value is 1 or 2 then persistent connections are requested. +If the value is 2 then persistent connections are required, +i.e., in case the server does not grant them an error occurs. +The default value is 1, which means preferring to keep the connection open. + =item B<-msg_timeout> I Number of seconds (or 0 for infinite) a CMP request-response message round trip is allowed to take before a timeout error is returned. -Default is 120. +Default is to use the B<-total_timeout> setting. =item B<-total_timeout> I diff --git a/doc/man3/ASN1_item_d2i_bio.pod b/doc/man3/ASN1_item_d2i_bio.pod index 4ec7da838c..bd3c9b06c2 100644 --- a/doc/man3/ASN1_item_d2i_bio.pod +++ b/doc/man3/ASN1_item_d2i_bio.pod @@ -31,7 +31,7 @@ ASN1_item_i2d_mem_bio() returns a pointer to a memory BIO or NULL on error. =head1 HISTORY -The functions described here were added in OpenSSL 3.0. +ASN1_item_i2d_mem_bio() was added in OpenSSL 3.0. =head1 COPYRIGHT diff --git a/doc/man3/OCSP_sendreq_new.pod b/doc/man3/OCSP_sendreq_new.pod index 10c6131f86..51469661de 100644 --- a/doc/man3/OCSP_sendreq_new.pod +++ b/doc/man3/OCSP_sendreq_new.pod @@ -18,10 +18,7 @@ OCSP_REQ_CTX_set1_req #include OSSL_HTTP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, - const OCSP_REQUEST *req, int maxline); - - int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OSSL_HTTP_REQ_CTX *rctx); - + const OCSP_REQUEST *req, int buf_size); OCSP_RESPONSE *OCSP_sendreq_bio(BIO *io, const char *path, OCSP_REQUEST *req); Deprecated since OpenSSL 3.0, can be hidden entirely by defining @@ -29,12 +26,12 @@ B with a suitable version value, see L: typedef OSSL_HTTP_REQ_CTX OCSP_REQ_CTX; + int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OSSL_HTTP_REQ_CTX *rctx); int OCSP_REQ_CTX_i2d(OCSP_REQ_CT *rctx, const ASN1_ITEM *it, ASN1_VALUE *req); int OCSP_REQ_CTX_add1_header(OCSP_REQ_CT *rctx, const char *name, const char *value); void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx); - void OCSP_set_max_response_length(OCSP_REQ_CT *rctx, - unsigned long len); + void OCSP_set_max_response_length(OCSP_REQ_CT *rctx, unsigned long len); int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, const OCSP_REQUEST *req); =head1 DESCRIPTION @@ -42,28 +39,32 @@ L: These functions perform an OCSP POST request / response transfer over HTTP, using the HTTP request functions described in L. -The function OCSP_sendreq_new() builds a complete B -structure using connection B I, the URL path I, the OCSP -request I, and with a response header maximum line length of I. -If I is zero a default value of 4k is used. +The function OCSP_sendreq_new() builds a complete B structure +with the B I to be used for requests and reponse, the URL path I, +optionally the OCSP request I, and a response header maximum line length +of I. If I is zero a default value of 4KiB is used. The I may be set to NULL and provided later using OCSP_REQ_CTX_set1_req() -or L . - +or L. The I and I arguments to OCSP_sendreq_new() correspond to the components of the URL. For example if the responder URL is C the BIO -I should be connected to host C on port 80 and I +I should haven been connected to host C on port 80 and I should be set to C. -OCSP_sendreq_nbio() performs I/O on the OCSP request context I. -When the operation is complete it assigns the response, a pointer to a -B structure, in I<*presp>. - -OCSP_sendreq_bio() is the same as a call to OCSP_sendreq_new() followed by -OCSP_sendreq_nbio() and then OCSP_REQ_CTX_free() in a single call, with a +OCSP_sendreq_nbio() attempts to send the request prepared in I +and to gather the response via HTTP, using the BIO I and I +that were given when calling OCSP_sendreq_new(). +If the operation gets completed it assigns the response, +a pointer to a B structure, in I<*presp>. +The function may need to be called again if its result is -1, which indicates +L. In such a case it is advisable to sleep a little in +between, using L on the read BIO to prevent a busy loop. + +OCSP_sendreq_bio() combines OCSP_sendreq_new() with as many calls of +OCSP_sendreq_nbio() as needed and then OCSP_REQ_CTX_free(), with a response header maximum line length 4k. It waits indefinitely on a response. It does not support setting a timeout or adding headers and is retained -for compatibility; use OCSP_sendreq_nbio() instead. +for compatibility; use L instead. OCSP_REQ_CTX_i2d(rctx, it, req) is equivalent to the following: @@ -88,15 +89,14 @@ L. OCSP_sendreq_new() returns a valid B structure or NULL if an error occurred. -OCSP_sendreq_nbio(), OCSP_REQ_CTX_i2d(), and OCSP_REQ_CTX_set1_req() -return 1 for success and 0 for failure. +OCSP_sendreq_nbio() returns 1 for success, 0 on error, -1 if retry is needed. OCSP_sendreq_bio() returns the B structure sent by the responder or NULL if an error occurred. =head1 SEE ALSO -L +L, L, L, L, L, diff --git a/doc/man3/OSSL_HTTP_REQ_CTX.pod b/doc/man3/OSSL_HTTP_REQ_CTX.pod index 8e928f19fa..a09b9b81a9 100644 --- a/doc/man3/OSSL_HTTP_REQ_CTX.pod +++ b/doc/man3/OSSL_HTTP_REQ_CTX.pod @@ -7,11 +7,15 @@ OSSL_HTTP_REQ_CTX_new, OSSL_HTTP_REQ_CTX_free, OSSL_HTTP_REQ_CTX_set_request_line, OSSL_HTTP_REQ_CTX_add1_header, +OSSL_HTTP_REQ_CTX_set_expected, OSSL_HTTP_REQ_CTX_set1_req, OSSL_HTTP_REQ_CTX_nbio, -OSSL_HTTP_REQ_CTX_sendreq_d2i, +OSSL_HTTP_REQ_CTX_nbio_d2i, +OSSL_HTTP_REQ_CTX_exchange, OSSL_HTTP_REQ_CTX_get0_mem_bio, -OSSL_HTTP_REQ_CTX_set_max_response_length +OSSL_HTTP_REQ_CTX_get_resp_len, +OSSL_HTTP_REQ_CTX_set_max_response_length, +OSSL_HTTP_is_alive - HTTP client low-level functions =head1 SYNOPSIS @@ -20,11 +24,7 @@ OSSL_HTTP_REQ_CTX_set_max_response_length typedef struct ossl_http_req_ctx_st OSSL_HTTP_REQ_CTX; - OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, - int maxline, unsigned long max_resp_len, - int timeout, - const char *expected_content_type, - int expect_asn1); + OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int buf_size); void OSSL_HTTP_REQ_CTX_free(OSSL_HTTP_REQ_CTX *rctx); int OSSL_HTTP_REQ_CTX_set_request_line(OSSL_HTTP_REQ_CTX *rctx, int method_POST, @@ -33,42 +33,41 @@ OSSL_HTTP_REQ_CTX_set_max_response_length int OSSL_HTTP_REQ_CTX_add1_header(OSSL_HTTP_REQ_CTX *rctx, const char *name, const char *value); + int OSSL_HTTP_REQ_CTX_set_expected(OSSL_HTTP_REQ_CTX *rctx, + const char *content_type, int asn1, + int timeout, int keep_alive); int OSSL_HTTP_REQ_CTX_set1_req(OSSL_HTTP_REQ_CTX *rctx, const char *content_type, - const ASN1_ITEM *it, ASN1_VALUE *req); + const ASN1_ITEM *it, const ASN1_VALUE *req); int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx); - ASN1_VALUE *OSSL_HTTP_REQ_CTX_sendreq_d2i(OSSL_HTTP_REQ_CTX *rctx, - const ASN1_ITEM *it); + int OSSL_HTTP_REQ_CTX_nbio_d2i(OSSL_HTTP_REQ_CTX *rctx, + ASN1_VALUE **pval, const ASN1_ITEM *it); + BIO *OSSL_HTTP_REQ_CTX_exchange(OSSL_HTTP_REQ_CTX *rctx); BIO *OSSL_HTTP_REQ_CTX_get0_mem_bio(const OSSL_HTTP_REQ_CTX *rctx); + size_t OSSL_HTTP_REQ_CTX_get_resp_len(const OSSL_HTTP_REQ_CTX *rctx); void OSSL_HTTP_REQ_CTX_set_max_response_length(OSSL_HTTP_REQ_CTX *rctx, unsigned long len); + int OSSL_HTTP_is_alive(const OSSL_HTTP_REQ_CTX *rctx); + =head1 DESCRIPTION -B is a context structure for an HTTP request, used to -collect all the necessary data to perform that request. +B is a context structure for an HTTP request and response, +used to collect all the necessary data to perform that request. This file documents low-level HTTP functions rarely used directly. High-level HTTP client functions like L and L should be preferred. OSSL_HTTP_REQ_CTX_new() allocates a new HTTP request context structure, -which gets populated with the B to send the request to (I), -the B to read the response from (I, which may be equal to I), -the maximum expected response header line length (I, where a value <= 0 -indicates that the B of 4KiB should be used; -this length is also used as the number of content bytes read at a time), -the maximum allowed response content length (I, where 0 means -that the B is used, which currently is 100 KiB), -a response timeout measure in seconds (I, -where 0 indicates no timeout, i.e., waiting indefinitely), -the expected MIME content type of the response (I, -which may be NULL for no expectation), -and a flag indicating that the response is expected to be -a DER encoded ASN.1 structure (I). +which gets populated with the B to write/send the request to (I), +the B to read/receive the response from (I, which may be equal to +I), and the maximum expected response header line length I. +A value <= 0 indicates that +the B of 4KiB should be used. +I is also used as the number of content bytes that are read at a time. The allocated context structure is also populated with an internal allocated memory B, which collects the HTTP request and additional headers as text. -The returned context should only be used for a single HTTP request/response. OSSL_HTTP_REQ_CTX_free() frees up the HTTP request context I. The I and I are not free'd and it is up to the application @@ -87,33 +86,71 @@ For example, to add a C header for C you would call: OSSL_HTTP_REQ_CTX_add1_header(ctx, "Host", "example.com"); +OSSL_HTTP_REQ_CTX_set_expected() optionally sets in I some expectations +of the HTTP client on the response. +Due to the structure of an HTTP request, if the I argument is +nonzero the function must be used before calling OSSL_HTTP_REQ_CTX_set1_req(). +If the I parameter +is not NULL then the client will check that the given content type string +is included in the HTTP header of the response and return an error if not. +If the I parameter is nonzero a structure in ASN.1 encoding will be +expected as the response content and input streaming is disabled. This means +that an ASN.1 sequence header is required, its length field is checked, and +OSSL_HTTP_REQ_CTX_get0_mem_bio() should be used to get the buffered response. +Else any form of input is allowed without length checks, which is the default. +In this case the BIO given as I argument to OSSL_HTTP_REQ_CTX_new() should +be used directly to read the response contents, which may support streaming. +If the I parameter is > 0 this indicates the maximum number of seconds +the subsequent HTTP transfer (sending the request and receiving a response) +is allowed to take. +A value <= 0 enables waiting indefinitely, i.e., no timeout can occur. +This is the default. +If the I parameter is 0, which is the default, the connection is not +kept open after receiving a response. This is the default behavior for HTTP 1.0. +If the value is 1 or 2 then a persistent connection is requested. +If the value is 2 then a persistent connection is required, +i.e., an error occurs in case the server does not grant it. + OSSL_HTTP_REQ_CTX_set1_req() is to be used if and only if the I -parameter in the OSSL_HTTP_REQ_CTX_set_request_line() call was 1. +parameter in the OSSL_HTTP_REQ_CTX_set_request_line() call was 1 +and an ASN.1-encoded request should be sent, which does not support streaming. It finalizes the HTTP request context by adding the DER encoding of I, using the ASN.1 template I to do the encoding. The HTTP header C is filled out with the length of the request. If I isn't NULL, -the HTTP header C is also added with its content as value. +the HTTP header C is also added with the given string value. All of this ends up in the internal memory B. -OSSL_HTTP_REQ_CTX_nbio() attempts to send the request prepared I -and gathering the response via HTTP, using the I and I +OSSL_HTTP_REQ_CTX_nbio() attempts to send the request prepared in I +and to gather the response via HTTP, using the I and I that were given when calling OSSL_HTTP_REQ_CTX_new(). -When successful, the contents of the internal memory B contains -the contents of the HTTP response, without the response headers. -It may need to be called again if its result is -1, which indicates +The function may need to be called again if its result is -1, which indicates L. In such a case it is advisable to sleep a little in -between using L on the read BIO to prevent a busy loop. - -OSSL_HTTP_REQ_CTX_sendreq_d2i() calls OSSL_HTTP_REQ_CTX_nbio(), possibly -several times until a timeout is reached, and DER decodes the received -response using the ASN.1 template I. +between, using L on the read BIO to prevent a busy loop. + +OSSL_HTTP_REQ_CTX_nbio_d2i() is like OSSL_HTTP_REQ_CTX_nbio() but on successs +in addition parses the response, which must be a DER-encoded ASN.1 structure, +using the ASN.1 template I and places the result in I<*pval>. + +OSSL_HTTP_REQ_CTX_exchange() calls OSSL_HTTP_REQ_CTX_nbio() as often as needed +in order to exchange a request and response or until a timeout is reached. +If successful and an ASN.1-encoded response was expected, the response contents +should be read via the BIO returned by OSSL_HTTP_REQ_CTX_get0_mem_bio(). +Else the I that was given when calling OSSL_HTTP_REQ_CTX_new() +represents the current state of reading the response. +If OSSL_HTTP_REQ_CTX_exchange() was successful, this BIO has been read past the +end of the response headers, such that the actual response contents can be read +via this BIO, which may support streaming. OSSL_HTTP_REQ_CTX_get0_mem_bio() returns the internal memory B. Before sending the request, this could used to modify the HTTP request text. I -After receiving a response via HTTP, the BIO represents -the current state of reading the response headers and contents. +After receiving a response via HTTP, the BIO represents the current state of +reading the response headers. If the response was expected to be ASN.1 encoded, +its contents can be read via this BIO, which does not support streaming. + +OSSL_HTTP_REQ_CTX_get_resp_len() returns the size of the response contents +in I if provided by the server as header field, else 0. OSSL_HTTP_REQ_CTX_set_max_response_length() sets the maximum allowed response content length for I to I. If not set or I is 0 @@ -122,6 +159,18 @@ If the C header is present and exceeds this value or the content is an ASN.1 encoded structure with a length exceeding this value or both length indications are present but disagree then an error occurs. +OSSL_HTTP_is_alive() can be used to query if the HTTP connection +given by I is still alive, i.e., has not been closed. +It returns 0 if I is NULL. + +If the client application requested or required a persistent connection +and this was granted by the server, it can keep I as long as it wants +to send further requests and OSSL_HTTP_is_alive() returns nonzero, +else it should call I or L. +In case the client application keeps I but the connection then dies +for any reason at the server side, it will notice this obtaining an +I/O error when trying to send the next request via I. + =head1 WARNINGS The server's response may be unexpected if the hostname that was used to @@ -155,7 +204,7 @@ and must be done exactly once in that case. =back When the request context is fully prepared, the HTTP exchange may be performed -with OSSL_HTTP_REQ_CTX_nbio() or OSSL_HTTP_REQ_CTX_sendreq_d2i(). +with OSSL_HTTP_REQ_CTX_nbio() or OSSL_HTTP_REQ_CTX_exchange(). =head1 RETURN VALUES @@ -166,20 +215,36 @@ OSSL_HTTP_REQ_CTX_free() and OSSL_HTTP_REQ_CTX_set_max_response_length() do not return values. OSSL_HTTP_REQ_CTX_set_request_line(), OSSL_HTTP_REQ_CTX_add1_header(), -OSSL_HTTP_REQ_CTX_set1_req() and OSSL_HTTP_REQ_CTX_nbio +OSSL_HTTP_REQ_CTX_set1_req(), and OSSL_HTTP_REQ_CTX_set_expected() return 1 for success and 0 for failure. -OSSL_HTTP_REQ_CTX_sendreq_d2i() returns a pointer to an B for -success and NULL for failure. +OSSL_HTTP_REQ_CTX_nbio() and OSSL_HTTP_REQ_CTX_nbio_d2i() +return 1 for success, 0 on error or redirection, -1 if retry is needed. -OSSL_HTTP_REQ_CTX_get0_mem_bio() returns the internal memory B. +OSSL_HTTP_REQ_CTX_exchange() and OSSL_HTTP_REQ_CTX_get0_mem_bio() +returns a pointer to a B on success and NULL on failure. + +OSSL_HTTP_REQ_CTX_get_resp_len() returns the size of the response contents +or 0 if not available or an error occurred. + +OSSL_HTTP_is_alive() returns 1 if its argument is non-NULL +and the client requested a persistent connection +and the server did not disagree on keeping the connection open, else 0. =head1 SEE ALSO L, L, +L, +L, +L, L, -L +L, +L + +=head1 HISTORY + +The functions described here were added in OpenSSL 3.0. =head1 COPYRIGHT diff --git a/doc/man3/OSSL_HTTP_transfer.pod b/doc/man3/OSSL_HTTP_transfer.pod index d2ff8eeebc..da84789472 100644 --- a/doc/man3/OSSL_HTTP_transfer.pod +++ b/doc/man3/OSSL_HTTP_transfer.pod @@ -2,13 +2,15 @@ =head1 NAME +OSSL_HTTP_open, +OSSL_HTTP_bio_cb_t, +OSSL_HTTP_proxy_connect, +OSSL_HTTP_set_request, +OSSL_HTTP_exchange, OSSL_HTTP_get, -OSSL_HTTP_get_asn1, -OSSL_HTTP_post_asn1, OSSL_HTTP_transfer, -OSSL_HTTP_bio_cb_t, -OSSL_HTTP_proxy_connect -- http client functions +OSSL_HTTP_close +- HTTP client high-level functions =head1 SYNOPSIS @@ -16,91 +18,53 @@ OSSL_HTTP_proxy_connect typedef BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail); + OSSL_HTTP_REQ_CTX *OSSL_HTTP_open(const char *server, const char *port, + const char *proxy, const char *no_proxy, + int use_ssl, BIO *bio, BIO *rbio, + OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, + int buf_size, int overall_timeout); + int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port, + const char *proxyuser, const char *proxypass, + int timeout, BIO *bio_err, const char *prog); + int OSSL_HTTP_set_request(OSSL_HTTP_REQ_CTX *rctx, const char *path, + const STACK_OF(CONF_VALUE) *headers, + const char *content_type, BIO *req, + const char *expected_content_type, int expect_asn1, + size_t max_resp_len, int timeout, int keep_alive); + BIO *OSSL_HTTP_exchange(OSSL_HTTP_REQ_CTX *rctx, char **redirection_url); BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy, BIO *bio, BIO *rbio, OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, - const STACK_OF(CONF_VALUE) *headers, - int maxline, unsigned long max_resp_len, int timeout, - const char *expected_ct, int expect_asn1); - ASN1_VALUE *OSSL_HTTP_get_asn1(const char *url, - const char *proxy, const char *no_proxy, - BIO *bio, BIO *rbio, - OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, - const STACK_OF(CONF_VALUE) *headers, - int maxline, unsigned long max_resp_len, - int timeout, const char *expected_ct, - const ASN1_ITEM *rsp_it); - ASN1_VALUE *OSSL_HTTP_post_asn1(const char *server, const char *port, - const char *path, int use_ssl, - const char *proxy, const char *no_proxy, - BIO *bio, BIO *rbio, - OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, - const STACK_OF(CONF_VALUE) *headers, - const char *content_type, - const ASN1_VALUE *req, const ASN1_ITEM *req_it, - int maxline, unsigned long max_resp_len, - int timeout, const char *expected_ct, - const ASN1_ITEM *rsp_it); - BIO *OSSL_HTTP_transfer(const char *server, const char *port, const char *path, - int use_ssl, const char *proxy, const char *no_proxy, + int buf_size, const STACK_OF(CONF_VALUE) *headers, + const char *expected_content_type, int expect_asn1, + size_t max_resp_len, int timeout); + BIO *OSSL_HTTP_transfer(OSSL_HTTP_REQ_CTX **prctx, + const char *server, const char *port, + const char *path, int use_ssl, + const char *proxy, const char *no_proxy, BIO *bio, BIO *rbio, OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, - const STACK_OF(CONF_VALUE) *headers, - const char *content_type, BIO *req_mem, - int maxline, unsigned long max_resp_len, int timeout, - const char *expected_ct, int expect_asn1, - char **redirection_url); - int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port, - const char *proxyuser, const char *proxypass, - int timeout, BIO *bio_err, const char *prog); + int buf_size, const STACK_OF(CONF_VALUE) *headers, + const char *content_type, BIO *req, + const char *expected_content_type, int expect_asn1, + size_t max_resp_len, int timeout, int keep_alive); + int OSSL_HTTP_close(OSSL_HTTP_REQ_CTX *rctx, int ok); =head1 DESCRIPTION -OSSL_HTTP_get() uses HTTP GET to obtain data (of any type) from the given I -and returns it as a memory BIO. -If the schema component of the I is C a TLS connection is requested -and the I parameter, described below, must be provided. -Any userinfo and fragment components in the I are ignored. -Any query component is handled as part of the path component. - -OSSL_HTTP_get_asn1() is like OSSL_HTTP_get() but in addition -parses the received contents (e.g., an X.509 certificate) -as an ASN.1 DER encoded value with the expected structure specified by I -and returns it on success as a pointer to I. - -OSSL_HTTP_post_asn1() is like OSSL_HTTP_get_asn1() but uses the HTTP POST method -to send a request I with the ASN.1 structure defined in I and the -given I to the given I and optional I and I. -If I is nonzero a TLS connection is requested and the I -parameter, described below, must be provided. +OSSL_HTTP_open() initiates an HTTP session using the I argument if not +NULL, else by connecting to a given I optionally via a I. -OSSL_HTTP_transfer() exchanges any form of HTTP request and response. -It implements the core of the functions described above. -If I parameter is NULL it defaults to "/". -If I is nonzero a TLS connection is requested -and the I parameter, described below, must be provided. -If I is NULL it uses the HTTP GET method, else it uses HTTP POST to -send a request with the contents of the memory BIO and optional I. -The optional list I may contain additional custom HTTP header lines. -If I is NULL (i.e., the HTTP method is GET) and I -is not NULL the latter pointer is used to provide any new location that -the server may return with HTTP code 301 (MOVED_PERMANENTLY) or 302 (FOUND). -In this case the caller is responsible for deallocating this URL with -L. - -The above functions have the following parameters in common. - -Typically the OpenSSL build supports sockets -and the I and I parameters are both NULL. -In this case the client creates a network BIO internally -for connecting to the given I -at the specified I (if any, defaulting to 80 for HTTP or 443 for HTTPS), -optionally via a I (respecting I) as described below. -Then the client uses this internal BIO for exchanging the request and response. -If I is given and I is NULL then the client uses this I instead. +Typically the OpenSSL build supports sockets and the I parameter is NULL. +In this case I must be NULL as well, and the +library creates a network BIO internally for connecting to the given I +at the specified I if any, defaulting to 80 for HTTP or 443 for HTTPS. +Then this internal BIO is used for setting up a connection +and for exchanging one or more request and response. +If I is given and I is NULL then this I is used instead. If both I and I are given (which may be memory BIOs for instance) -then no explicit connection is attempted, -I is used for writing the request, and I for reading the response. +then no explicit connection is set up, but +I is used for writing requests and I for reading responses. As soon as the client has flushed I the server must be ready to provide a response or indicate a waiting condition via I. @@ -121,33 +85,12 @@ Proxying plain HTTP is supported directly, while using a proxy for HTTPS connections requires a suitable callback function such as OSSL_HTTP_proxy_connect(), described below. -The I parameter specifies the response header maximum line length, -where a value <= 0 indicates that the B of 4KiB -should be used. -This length is also used as the number of content bytes that are read at a time. -The I parameter specifies the maximum response length, -where 0 indicates B, which currently is 100 KiB. - -An ASN.1-encoded response is expected by OSSL_HTTP_get_asn1() and -OSSL_HTTP_post_asn1(), while for OSSL_HTTP_get() or OSSL_HTTP_transfer() -this is only the case if the I parameter is nonzero. -If the response header contains one or more "Content-Length" header lines and/or -an ASN.1-encoded response is expected, which should include a total length, -the length indications received are checked for consistency -and for not exceeding the maximum response length. - -If the parameter I -is not NULL then the HTTP client checks that the given content type string -is included in the HTTP header of the response and returns an error if not. - -If the I parameter is > 0 this indicates the maximum number of seconds -to wait until the transfer is complete. -A value of 0 enables waiting indefinitely, -while a value < 0 immediately leads to a timeout condition. +If I is nonzero a TLS connection is requested +and the I parameter must be provided. -The optional parameter I with its optional argument I may -be used to modify the connection BIO used by the HTTP client (and cannot be -used when both I and I are given). +The parameter I, which is optional if I is 0, +may be used to modify the connection BIO used by the HTTP client, +but cannot be used when both I and I are given. I is a BIO connect/disconnect callback function with prototype BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail) @@ -157,7 +100,7 @@ whereby it may make use of a custom defined argument I, which may for instance refer to an I structure. During connection establishment, just after calling BIO_do_connect_retry(), the function is invoked with the I argument being 1 and the I -argument being 1 if HTTPS is requested, i.e., SSL/TLS should be enabled. +argument being 1 if HTTPS is requested, i.e., SSL/TLS should be enabled, else 0. On disconnect I is 0 and I is 1 if no error occurred, else 0. For instance, on connect the function may prepend a TLS BIO to implement HTTPS; after disconnect it may do some diagnostic output and/or specific cleanup. @@ -166,10 +109,10 @@ Here is a simple example that supports TLS connections (but not via a proxy): BIO *http_tls_cb(BIO *hbio, void *arg, int connect, int detail) { - SSL_CTX *ctx = (SSL_CTX *)arg; - if (connect && detail) { /* connecting with TLS */ + SSL_CTX *ctx = (SSL_CTX *)arg; BIO *sbio = BIO_new_ssl(ctx, 1); + hbio = sbio != NULL ? BIO_push(sbio, hbio) : NULL; } else if (!connect && !detail) { /* disconnecting after error */ /* optionally add diagnostics here */ @@ -179,6 +122,16 @@ Here is a simple example that supports TLS connections (but not via a proxy): After disconnect the modified BIO will be deallocated using BIO_free_all(). +The I parameter specifies the response header maximum line length. +A value <= 0 indicates that +the B of 4KiB should be used. +I is also used as the number of content bytes that are read at a time. + +If the I parameter is > 0 this indicates the maximum number of +seconds the overall HTTP transfer (i.e., connection setup if needed, +sending requests, and receiving responses) is allowed to take until completion. +A value <= 0 enables waiting indefinitely, i.e., no timeout. + OSSL_HTTP_proxy_connect() may be used by an above BIO connect callback function to set up an SSL/TLS connection via an HTTPS proxy. It promotes the given BIO I representing a connection @@ -186,11 +139,86 @@ pre-established with a TLS proxy using the HTTP CONNECT method, optionally using proxy client credentials I and I, to connect with TLS protection ultimately to I and I. If the I argument is NULL or the empty string it defaults to "443". -The I parameter is used as described above. +If the I parameter is > 0 this indicates the maximum number of +seconds the connection setup is allowed to take. +A value <= 0 enables waiting indefinitely, i.e., no timeout. Since this function is typically called by applications such as L it uses the I and I parameters (unless NULL) to print additional diagnostic information in a user-oriented way. +OSSL_HTTP_set_request() sets up in I the request header and content data +and expectations on the response using the following parameters. +If I is NULL it defaults to "/". +If I is NULL the HTTP GET method will be used to send the request +else HTTP POST with the contents of I and optional I, where +the length of the data in I does not need to be determined in advance: the +BIO will be read on-the-fly while sending the request, which supports streaming. +The optional list I may contain additional custom HTTP header lines. +If the parameter I +is not NULL then the client will check that the given content type string +is included in the HTTP header of the response and return an error if not. +If the I parameter is nonzero, +a structure in ASN.1 encoding will be expected as response content. +The I parameter specifies the maximum allowed +response content length, where the value 0 indicates no limit. +If the I parameter is > 0 this indicates the maximum number of seconds +the subsequent HTTP transfer (sending the request and receiving a response) +is allowed to take. +A value of 0 enables waiting indefinitely, i.e., no timeout. +A value < 0 indicates that the I parameter value given +when opening the HTTP transfer will be used instead. +If I is 0 the connection is not kept open +after receiving a response, which is the default behavior for HTTP 1.0. +If the value is 1 or 2 then a persistent connection is requested. +If the value is 2 then a persistent connection is required, +i.e., an error occurs in case the server does not grant it. + +OSSL_HTTP_exchange() exchanges any form of HTTP request and response +as specified by I, which must include both connection and request data, +typically set up using OSSL_HTTP_open() and OSSL_HTTP_set_request(). +It implements the core of the functions described below. +If the HTTP method is GET and I +is not NULL the latter pointer is used to provide any new location that +the server may return with HTTP code 301 (MOVED_PERMANENTLY) or 302 (FOUND). +In this case the function returns NULL and the caller is +responsible for deallocating the URL with L. +If the response header contains one or more "Content-Length" header lines and/or +an ASN.1-encoded response is expected, which should include a total length, +the length indications received are checked for consistency +and for not exceeding any given maximum response length. +On receiving a response, the function returns the contents as a memory BIO, +which does not support streaming, in case an ASN.1-encoded response is expected. +Else it returns directly the read BIO that holds the response contents, +which allows a response of indefinite length and may support streaming. + +OSSL_HTTP_get() uses HTTP GET to obtain data from I if non-NULL, +else from the server contained in the I, and returns it as a BIO. +It supports redirection via HTTP status code 301 or 302. It is meant for +transfers with a single round trip, so does not support persistent connections. +If I is non-NULL, any host and port components in the I are not used +for connecting but the hostname is used, as usual, for the C header. +Any userinfo and fragment components in the I are ignored. +Any query component is handled as part of the path component. +If the scheme component of the I is C a TLS connection is requested +and the I, as described for OSSL_HTTP_open(), must be provided. +Also the remaining parameters are interpreted as described for OSSL_HTTP_open() +and OSSL_HTTP_set_request(), respectively. + +OSSL_HTTP_transfer() exchanges an HTTP request and response +over a connection managed via I without supporting redirection. +It combines OSSL_HTTP_open(), OSSL_HTTP_set_request(), OSSL_HTTP_exchange(), +and OSSL_HTTP_close(). +If I is not NULL it reuses any open connection represented by a non-NULL +I<*prctx>. It keeps the connection open if a persistent connection is requested +or required and this was granted by the server, else it closes the connection +and assigns NULL to I<*prctx>. +The remaining parameters are interpreted as described for OSSL_HTTP_open() +and OSSL_HTTP_set_request(), respectively. + +OSSL_HTTP_close() closes the connection and releases I. +The I parameter is passed to any BIO update function +given during setup as described above for OSSL_HTTP_open(). + =head1 NOTES The names of the environment variables used by this implementation: @@ -200,23 +228,29 @@ other HTTP client implementations such as wget, curl, and git. =head1 RETURN VALUES -On success, OSSL_HTTP_get(), OSSL_HTTP_get_asn1(), OSSL_HTTP_post_asn1(), and -OSSL_HTTP_transfer() return a memory BIO containing the data received via HTTP. -This must be freed by the caller. On failure, NULL is returned. +OSSL_HTTP_open() returns on success a B, else NULL. + +OSSL_HTTP_proxy_connect() and OSSL_HTTP_set_request() +return 1 on success, 0 on error. + +On success, OSSL_HTTP_exchange(), OSSL_HTTP_get(), and OSSL_HTTP_transfer() +return a memory BIO containing the data received if an ASN.1-encoded response +is expected, else a BIO that may support streaming. +The BIO must be freed by the caller. +On failure, they return NULL. Failure conditions include connection/transfer timeout, parse errors, etc. -OSSL_HTTP_proxy_connect() returns 1 on success, 0 on error. +OSSL_HTTP_close() returns 0 if anything went wrong while disconnecting, else 1. =head1 SEE ALSO -L -L +L, L +L, L, +L =head1 HISTORY -OSSL_HTTP_get(), OSSL_HTTP_get_asn1(), OSSL_HTTP_post_asn1(), -OSSL_HTTP_transfer(), and OSSL_HTTP_proxy_connect() -were added in OpenSSL 3.0. +All the functions described here were added in OpenSSL 3.0. =head1 COPYRIGHT diff --git a/doc/man3/X509_load_http.pod b/doc/man3/X509_load_http.pod index 9e54d31c42..93a63c68cf 100644 --- a/doc/man3/X509_load_http.pod +++ b/doc/man3/X509_load_http.pod @@ -49,7 +49,7 @@ Error conditions include connection/transfer timeout, parse errors, etc. =head1 SEE ALSO -L +L =head1 HISTORY diff --git a/include/crypto/httperr.h b/include/crypto/httperr.h index 648f55c691..c68ca3b0c4 100644 --- a/include/crypto/httperr.h +++ b/include/crypto/httperr.h @@ -1,6 +1,6 @@ /* * Generated by util/mkerr.pl DO NOT EDIT - * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy diff --git a/include/openssl/cmp.h.in b/include/openssl/cmp.h.in index 2591963b6f..352ffcdb2f 100644 --- a/include/openssl/cmp.h.in +++ b/include/openssl/cmp.h.in @@ -262,25 +262,29 @@ void OSSL_CMP_MSG_free(OSSL_CMP_MSG *msg); OSSL_CMP_CTX *OSSL_CMP_CTX_new(OSSL_LIB_CTX *libctx, const char *propq); void OSSL_CMP_CTX_free(OSSL_CMP_CTX *ctx); int OSSL_CMP_CTX_reinit(OSSL_CMP_CTX *ctx); -/* various CMP options: */ +/* CMP general options: */ # define OSSL_CMP_OPT_LOG_VERBOSITY 0 -# define OSSL_CMP_OPT_MSG_TIMEOUT 1 -# define OSSL_CMP_OPT_TOTAL_TIMEOUT 2 -# define OSSL_CMP_OPT_VALIDITY_DAYS 3 -# define OSSL_CMP_OPT_SUBJECTALTNAME_NODEFAULT 4 -# define OSSL_CMP_OPT_SUBJECTALTNAME_CRITICAL 5 -# define OSSL_CMP_OPT_POLICIES_CRITICAL 6 -# define OSSL_CMP_OPT_POPO_METHOD 7 -# define OSSL_CMP_OPT_DIGEST_ALGNID 8 -# define OSSL_CMP_OPT_OWF_ALGNID 9 -# define OSSL_CMP_OPT_MAC_ALGNID 10 -# define OSSL_CMP_OPT_REVOCATION_REASON 11 -# define OSSL_CMP_OPT_IMPLICIT_CONFIRM 12 -# define OSSL_CMP_OPT_DISABLE_CONFIRM 13 -# define OSSL_CMP_OPT_UNPROTECTED_SEND 14 -# define OSSL_CMP_OPT_UNPROTECTED_ERRORS 15 -# define OSSL_CMP_OPT_IGNORE_KEYUSAGE 16 -# define OSSL_CMP_OPT_PERMIT_TA_IN_EXTRACERTS_FOR_IR 17 +/* CMP transfer options: */ +# define OSSL_CMP_OPT_KEEP_ALIVE 10 +# define OSSL_CMP_OPT_MSG_TIMEOUT 11 +# define OSSL_CMP_OPT_TOTAL_TIMEOUT 12 +/* CMP request options: */ +# define OSSL_CMP_OPT_VALIDITY_DAYS 20 +# define OSSL_CMP_OPT_SUBJECTALTNAME_NODEFAULT 21 +# define OSSL_CMP_OPT_SUBJECTALTNAME_CRITICAL 22 +# define OSSL_CMP_OPT_POLICIES_CRITICAL 23 +# define OSSL_CMP_OPT_POPO_METHOD 24 +# define OSSL_CMP_OPT_IMPLICIT_CONFIRM 25 +# define OSSL_CMP_OPT_DISABLE_CONFIRM 26 +# define OSSL_CMP_OPT_REVOCATION_REASON 27 +/* CMP protection options: */ +# define OSSL_CMP_OPT_UNPROTECTED_SEND 30 +# define OSSL_CMP_OPT_UNPROTECTED_ERRORS 31 +# define OSSL_CMP_OPT_OWF_ALGNID 32 +# define OSSL_CMP_OPT_MAC_ALGNID 33 +# define OSSL_CMP_OPT_DIGEST_ALGNID 34 +# define OSSL_CMP_OPT_IGNORE_KEYUSAGE 35 +# define OSSL_CMP_OPT_PERMIT_TA_IN_EXTRACERTS_FOR_IR 36 int OSSL_CMP_CTX_set_option(OSSL_CMP_CTX *ctx, int opt, int val); int OSSL_CMP_CTX_get_option(const OSSL_CMP_CTX *ctx, int opt); /* CMP-specific callback for logging and outputting the error queue: */ diff --git a/include/openssl/http.h b/include/openssl/http.h index 18d0f13b3e..2140d5d2f8 100644 --- a/include/openssl/http.h +++ b/include/openssl/http.h @@ -23,8 +23,6 @@ extern "C" { # endif -typedef BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail); - # define OSSL_HTTP_NAME "http" # define OSSL_HTTPS_NAME "https" # define OSSL_HTTP_PREFIX OSSL_HTTP_NAME"://" @@ -38,63 +36,64 @@ typedef BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail) #define HTTP_DEFAULT_MAX_LINE_LENGTH (4 * 1024) #define HTTP_DEFAULT_MAX_RESP_LEN (100 * 1024) -OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, - int maxline, unsigned long max_resp_len, - int timeout, const char *expected_ct, - int expect_asn1); +/* Low-level HTTP API */ +OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int buf_size); void OSSL_HTTP_REQ_CTX_free(OSSL_HTTP_REQ_CTX *rctx); int OSSL_HTTP_REQ_CTX_set_request_line(OSSL_HTTP_REQ_CTX *rctx, int method_POST, const char *server, const char *port, const char *path); int OSSL_HTTP_REQ_CTX_add1_header(OSSL_HTTP_REQ_CTX *rctx, const char *name, const char *value); +int OSSL_HTTP_REQ_CTX_set_expected(OSSL_HTTP_REQ_CTX *rctx, + const char *content_type, int asn1, + int timeout, int keep_alive); int OSSL_HTTP_REQ_CTX_set1_req(OSSL_HTTP_REQ_CTX *rctx, const char *content_type, - const ASN1_ITEM *it, ASN1_VALUE *req); + const ASN1_ITEM *it, const ASN1_VALUE *req); int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx); -ASN1_VALUE *OSSL_HTTP_REQ_CTX_sendreq_d2i(OSSL_HTTP_REQ_CTX *rctx, - const ASN1_ITEM *it); +int OSSL_HTTP_REQ_CTX_nbio_d2i(OSSL_HTTP_REQ_CTX *rctx, + ASN1_VALUE **pval, const ASN1_ITEM *it); +BIO *OSSL_HTTP_REQ_CTX_exchange(OSSL_HTTP_REQ_CTX *rctx); BIO *OSSL_HTTP_REQ_CTX_get0_mem_bio(const OSSL_HTTP_REQ_CTX *rctx); +size_t OSSL_HTTP_REQ_CTX_get_resp_len(const OSSL_HTTP_REQ_CTX *rctx); void OSSL_HTTP_REQ_CTX_set_max_response_length(OSSL_HTTP_REQ_CTX *rctx, unsigned long len); +int OSSL_HTTP_is_alive(const OSSL_HTTP_REQ_CTX *rctx); +/* High-level HTTP API */ +typedef BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail); +OSSL_HTTP_REQ_CTX *OSSL_HTTP_open(const char *server, const char *port, + const char *proxy, const char *no_proxy, + int use_ssl, BIO *bio, BIO *rbio, + OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, + int buf_size, int overall_timeout); +int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port, + const char *proxyuser, const char *proxypass, + int timeout, BIO *bio_err, const char *prog); +int OSSL_HTTP_set_request(OSSL_HTTP_REQ_CTX *rctx, const char *path, + const STACK_OF(CONF_VALUE) *headers, + const char *content_type, BIO *req, + const char *expected_content_type, int expect_asn1, + size_t max_resp_len, int timeout, int keep_alive); +BIO *OSSL_HTTP_exchange(OSSL_HTTP_REQ_CTX *rctx, char **redirection_url); BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy, BIO *bio, BIO *rbio, OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, - const STACK_OF(CONF_VALUE) *headers, - int maxline, unsigned long max_resp_len, int timeout, - const char *expected_ct, int expect_asn1); -ASN1_VALUE *OSSL_HTTP_get_asn1(const char *url, - const char *proxy, const char *no_proxy, - BIO *bio, BIO *rbio, - OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, - const STACK_OF(CONF_VALUE) *headers, - int maxline, unsigned long max_resp_len, - int timeout, const char *expected_ct, - const ASN1_ITEM *rsp_it); -ASN1_VALUE *OSSL_HTTP_post_asn1(const char *server, const char *port, - const char *path, int use_ssl, - const char *proxy, const char *no_proxy, - BIO *bio, BIO *rbio, - OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, - const STACK_OF(CONF_VALUE) *headers, - const char *content_type, - const ASN1_VALUE *req, const ASN1_ITEM *req_it, - int maxline, unsigned long max_resp_len, - int timeout, const char *expected_ct, - const ASN1_ITEM *rsp_it); -BIO *OSSL_HTTP_transfer(const char *server, const char *port, const char *path, - int use_ssl, const char *proxy, const char *no_proxy, + int buf_size, const STACK_OF(CONF_VALUE) *headers, + const char *expected_content_type, int expect_asn1, + size_t max_resp_len, int timeout); +BIO *OSSL_HTTP_transfer(OSSL_HTTP_REQ_CTX **prctx, + const char *server, const char *port, + const char *path, int use_ssl, + const char *proxy, const char *no_proxy, BIO *bio, BIO *rbio, OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, - const STACK_OF(CONF_VALUE) *headers, - const char *content_type, BIO *req_mem, - int maxline, unsigned long max_resp_len, int timeout, - const char *expected_ct, int expect_asn1, - char **redirection_url); -int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port, - const char *proxyuser, const char *proxypass, - int timeout, BIO *bio_err, const char *prog); + int buf_size, const STACK_OF(CONF_VALUE) *headers, + const char *content_type, BIO *req, + const char *expected_content_type, int expect_asn1, + size_t max_resp_len, int timeout, int keep_alive); +int OSSL_HTTP_close(OSSL_HTTP_REQ_CTX *rctx, int ok); +/* Auxiliary functions */ int OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost, char **pport, int *pport_num, char **ppath, char **pquery, char **pfrag); diff --git a/include/openssl/httperr.h b/include/openssl/httperr.h index af5717d3dc..b639ef0051 100644 --- a/include/openssl/httperr.h +++ b/include/openssl/httperr.h @@ -29,6 +29,7 @@ # define HTTP_R_ERROR_RECEIVING 103 # define HTTP_R_ERROR_SENDING 102 # define HTTP_R_FAILED_READING_DATA 128 +# define HTTP_R_HEADER_PARSE_ERROR 126 # define HTTP_R_INCONSISTENT_CONTENT_LENGTH 120 # define HTTP_R_INVALID_PORT_NUMBER 123 # define HTTP_R_INVALID_URL_PATH 125 @@ -43,6 +44,7 @@ # define HTTP_R_REDIRECTION_NOT_ENABLED 116 # define HTTP_R_RESPONSE_LINE_TOO_LONG 113 # define HTTP_R_RESPONSE_PARSE_ERROR 104 +# define HTTP_R_SERVER_CANCELED_CONNECTION 127 # define HTTP_R_SOCK_NOT_SUPPORTED 122 # define HTTP_R_STATUS_CODE_UNSUPPORTED 114 # define HTTP_R_TLS_NOT_ENABLED 107 diff --git a/include/openssl/ocsp.h.in b/include/openssl/ocsp.h.in index 83c8a175fe..869c3ad415 100644 --- a/include/openssl/ocsp.h.in +++ b/include/openssl/ocsp.h.in @@ -170,34 +170,30 @@ typedef struct ocsp_service_locator_st OCSP_SERVICELOC; DECLARE_ASN1_DUP_FUNCTION(OCSP_CERTID) -OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req); OSSL_HTTP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, - const OCSP_REQUEST *req, int maxline); -int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OSSL_HTTP_REQ_CTX *rctx); + const OCSP_REQUEST *req, int buf_size); +OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req); # ifndef OPENSSL_NO_DEPRECATED_3_0 typedef OSSL_HTTP_REQ_CTX OCSP_REQ_CTX; -# define OCSP_REQ_CTX_new(io, maxline) \ - OSSL_HTTP_REQ_CTX_new(io, io, maxline, 0, 0, NULL, 1) -# define OCSP_REQ_CTX_free(r) \ - OSSL_HTTP_REQ_CTX_free(r) +# define OCSP_REQ_CTX_new(io, buf_size) \ + OSSL_HTTP_REQ_CTX_new(io, io, buf_size) +# define OCSP_REQ_CTX_free OSSL_HTTP_REQ_CTX_free # define OCSP_REQ_CTX_http(rctx, op, path) \ OSSL_HTTP_REQ_CTX_set_request_line(rctx, strcmp(op, "POST") == 0, \ NULL, NULL, path) -# define OCSP_REQ_CTX_add1_header(r, n, v) \ - OSSL_HTTP_REQ_CTX_add1_header(r, n, v) +# define OCSP_REQ_CTX_add1_header OSSL_HTTP_REQ_CTX_add1_header # define OCSP_REQ_CTX_i2d(r, it, req) \ OSSL_HTTP_REQ_CTX_set1_req(r, "application/ocsp-request", it, req) # define OCSP_REQ_CTX_set1_req(r, req) \ OCSP_REQ_CTX_i2d(r, ASN1_ITEM_rptr(OCSP_REQUEST), (ASN1_VALUE *)(req)) -# define OCSP_REQ_CTX_nbio(r) \ - OSSL_HTTP_REQ_CTX_nbio(r) -# define OCSP_REQ_CTX_nbio_d2i(r, p, i) \ - ((*(p) = OSSL_HTTP_REQ_CTX_sendreq_d2i(r, i)) != NULL) -# define OCSP_REQ_CTX_get0_mem_bio(r) \ - OSSL_HTTP_REQ_CTX_get0_mem_bio(r) -# define OCSP_set_max_response_length(r, l) \ - OSSL_HTTP_REQ_CTX_set_max_response_length(r, l) +# define OCSP_REQ_CTX_nbio OSSL_HTTP_REQ_CTX_nbio +# define OCSP_REQ_CTX_nbio_d2i OSSL_HTTP_REQ_CTX_nbio_d2i +# define OCSP_sendreq_nbio(r, p) \ + OSSL_HTTP_REQ_CTX_nbio_d2i(r, (ASN1_VALUE **)(p), \ + ASN1_ITEM_rptr(OCSP_RESPONSE)) +# define OCSP_REQ_CTX_get0_mem_bio OSSL_HTTP_REQ_CTX_get0_mem_bio +# define OCSP_set_max_response_length OSSL_HTTP_REQ_CTX_set_max_response_length # endif OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *dgst, const X509 *subject, diff --git a/test/cmp_ctx_test.c b/test/cmp_ctx_test.c index 2ca2c26dd5..e25aa9ab43 100644 --- a/test/cmp_ctx_test.c +++ b/test/cmp_ctx_test.c @@ -717,9 +717,8 @@ void cleanup_tests(void) return; } -DEFINE_SET_GET_ARG_FN(set, get, option, 16, int) -/* option == OSSL_CMP_OPT_IGNORE_KEYUSAGE */ -DEFINE_SET_GET_BASE_TEST(OSSL_CMP_CTX, set, get, 0, option_16, int, -1, IS_0, \ +DEFINE_SET_GET_ARG_FN(set, get, option, 35, int) /* OPT_IGNORE_KEYUSAGE */ +DEFINE_SET_GET_BASE_TEST(OSSL_CMP_CTX, set, get, 0, option_35, int, -1, IS_0, \ 1 /* true */, DROP) DEFINE_SET_CB_TEST(log_cb) @@ -792,7 +791,7 @@ int setup_tests(void) ADD_TEST(test_CTX_reinit); /* various CMP options: */ - ADD_TEST(test_CTX_set_get_option_16); + ADD_TEST(test_CTX_set_get_option_35); /* CMP-specific callback for logging and outputting the error queue: */ ADD_TEST(test_CTX_set_get_log_cb); /* diff --git a/test/http_test.c b/test/http_test.c index 0a3389c15f..e4209a37c0 100644 --- a/test/http_test.c +++ b/test/http_test.c @@ -99,36 +99,38 @@ static int test_http_x509(int do_get) X509 *rcert = NULL; BIO *wbio = BIO_new(BIO_s_mem()); BIO *rbio = BIO_new(BIO_s_mem()); + BIO *rsp, *req = ASN1_item_i2d_mem_bio(x509_it, (ASN1_VALUE *)x509); STACK_OF(CONF_VALUE) *headers = NULL; + const char content_type[] = "application/x-x509-ca-cert"; int res = 0; - if (wbio == NULL || rbio == NULL) + if (wbio == NULL || rbio == NULL || req == NULL) goto err; BIO_set_callback_ex(wbio, http_bio_cb_ex); BIO_set_callback_arg(wbio, (char *)rbio); rpath = RPATH; - rcert = (X509 *) - (do_get ? - OSSL_HTTP_get_asn1("http://"SERVER":"PORT"/"RPATH, - NULL /* proxy */, NULL /* no_proxy */, - wbio, rbio, NULL /* bio_update_fn */, NULL, - headers, 0 /* maxline */, - 0 /* max_resp_len */, 0 /* timeout */, - "application/x-x509-ca-cert", x509_it) - : - OSSL_HTTP_post_asn1(SERVER, PORT, RPATH, 0 /* use_ssl */, - NULL /* proxy */, NULL /* no_proxy */, - wbio, rbio, NULL /* bio_update_fn */, NULL, - headers, "application/x-x509-ca-cert", - (ASN1_VALUE *)x509, x509_it, 0 /* maxline */, - 0 /* max_resp_len */, 0 /* timeout */, - "application/x-x509-ca-cert", x509_it) - ); + rsp = do_get ? + OSSL_HTTP_get("http://"SERVER":"PORT"/"RPATH, + NULL /* proxy */, NULL /* no_proxy */, + wbio, rbio, NULL /* bio_fn */, NULL /* arg */, + 0 /* buf_size */, headers, content_type, + 1 /* expect_asn1 */, + HTTP_DEFAULT_MAX_RESP_LEN, 0 /* timeout */) + : OSSL_HTTP_transfer(NULL, NULL /* host */, NULL /* port */, RPATH, + 0 /* use_ssl */,NULL /* proxy */, NULL /* no_pr */, + wbio, rbio, NULL /* bio_fn */, NULL /* arg */, + 0 /* buf_size */, headers, content_type, + req, content_type, 1 /* expect_asn1 */, + HTTP_DEFAULT_MAX_RESP_LEN, 0 /* timeout */, + 0 /* keep_alive */); + rcert = d2i_X509_bio(rsp, NULL); + BIO_free(rsp); res = TEST_ptr(rcert) && TEST_int_eq(X509_cmp(x509, rcert), 0); err: X509_free(rcert); + BIO_free(req); BIO_free(wbio); BIO_free(rbio); sk_CONF_VALUE_pop_free(headers, X509V3_conf_free); diff --git a/util/libcrypto.num b/util/libcrypto.num index ac5e6dac31..69b8f63e32 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -614,7 +614,7 @@ UI_get0_result_string 629 3_0_0 EXIST::FUNCTION: TS_RESP_CTX_add_policy 630 3_0_0 EXIST::FUNCTION:TS X509_REQ_dup 631 3_0_0 EXIST::FUNCTION: d2i_DSA_PUBKEY_fp 633 3_0_0 EXIST::FUNCTION:DEPRECATEDIN_3_0,DSA,STDIO -OSSL_HTTP_REQ_CTX_sendreq_d2i 634 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_REQ_CTX_exchange 634 3_0_0 EXIST::FUNCTION: d2i_X509_REQ_fp 635 3_0_0 EXIST::FUNCTION:STDIO DH_OpenSSL 636 3_0_0 EXIST::FUNCTION:DEPRECATEDIN_3_0,DH BN_get_rfc3526_prime_8192 637 3_0_0 EXIST::FUNCTION: @@ -3612,7 +3612,7 @@ EVP_CIPHER_CTX_encrypting 3694 3_0_0 EXIST::FUNCTION: EC_KEY_can_sign 3695 3_0_0 EXIST::FUNCTION:DEPRECATEDIN_3_0,EC PEM_write_bio_RSAPublicKey 3696 3_0_0 EXIST::FUNCTION:DEPRECATEDIN_3_0 X509_CRL_set1_lastUpdate 3697 3_0_0 EXIST::FUNCTION: -OCSP_sendreq_nbio 3698 3_0_0 EXIST::FUNCTION:OCSP +OSSL_HTTP_REQ_CTX_nbio_d2i 3698 3_0_0 EXIST::FUNCTION: PKCS8_encrypt 3699 3_0_0 EXIST::FUNCTION: i2d_PKCS7_fp 3700 3_0_0 EXIST::FUNCTION:STDIO i2d_X509_REQ 3701 3_0_0 EXIST::FUNCTION: @@ -4882,11 +4882,16 @@ BIO_socket_wait ? 3_0_0 EXIST::FUNCTION:SOCK BIO_wait ? 3_0_0 EXIST::FUNCTION: BIO_do_connect_retry ? 3_0_0 EXIST::FUNCTION: OSSL_parse_url ? 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_REQ_CTX_get_resp_len ? 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_REQ_CTX_set_expected ? 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_is_alive ? 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_open ? 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_proxy_connect ? 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_set_request ? 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_exchange ? 3_0_0 EXIST::FUNCTION: OSSL_HTTP_get ? 3_0_0 EXIST::FUNCTION: -OSSL_HTTP_get_asn1 ? 3_0_0 EXIST::FUNCTION: -OSSL_HTTP_post_asn1 ? 3_0_0 EXIST::FUNCTION: OSSL_HTTP_transfer ? 3_0_0 EXIST::FUNCTION: -OSSL_HTTP_proxy_connect ? 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_close ? 3_0_0 EXIST::FUNCTION: ASN1_item_i2d_mem_bio ? 3_0_0 EXIST::FUNCTION: ERR_add_error_txt ? 3_0_0 EXIST::FUNCTION: ERR_add_error_mem_bio ? 3_0_0 EXIST::FUNCTION: diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt index cb5a9eaa6f..9eefc090f8 100644 --- a/util/missingcrypto.txt +++ b/util/missingcrypto.txt @@ -781,7 +781,6 @@ OCSP_REQ_CTX_get0_mem_bio(3) OCSP_REQ_CTX_http(3) OCSP_REQ_CTX_new(3) OCSP_REQ_CTX_nbio(3) -OCSP_REQ_CTX_nbio_d2i(3) OCSP_REQUEST_add1_ext_i2d(3) OCSP_REQUEST_add_ext(3) OCSP_REQUEST_delete_ext(3) diff --git a/util/other.syms b/util/other.syms index 0047905209..f8fb0d52e2 100644 --- a/util/other.syms +++ b/util/other.syms @@ -339,8 +339,9 @@ OCSP_REQ_CTX datatype deprecated 3.0.0 OCSP_REQ_CTX_add1_header define deprecated 3.0.0 OCSP_REQ_CTX_free define deprecated 3.0.0 OCSP_REQ_CTX_i2d define deprecated 3.0.0 -OCSP_set_max_response_length define deprecated 3.0.0 OCSP_REQ_CTX_set1_req define deprecated 3.0.0 +OCSP_sendreq_nbio define deprecated 3.0.0 +OCSP_set_max_response_length define deprecated 3.0.0 OPENSSL_FILE define OPENSSL_FUNC define OPENSSL_LINE define