diff options
45 files changed, 2495 insertions, 1016 deletions
diff --git a/apps/include/apps.h b/apps/include/apps.h index c33a98772b..78be647619 100644 --- a/apps/include/apps.h +++ b/apps/include/apps.h @@ -28,12 +28,14 @@ # include <openssl/txt_db.h> # include <openssl/engine.h> # include <openssl/ocsp.h> +# include <openssl/http.h> # include <signal.h> # include "apps_ui.h" # include "opt.h" # include "fmt.h" # include "platform.h" +/* also in include/internal/sockets.h */ # if defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_WINCE) # define openssl_fdset(a,b) FD_SET((unsigned int)a, b) # else @@ -215,6 +217,30 @@ void print_cert_checks(BIO *bio, X509 *x, void store_setup_crl_download(X509_STORE *st); +typedef struct app_http_tls_info_st { + const char *server; + const char *port; + int use_proxy; + long timeout; + SSL_CTX *ssl_ctx; +} APP_HTTP_TLS_INFO; +BIO *app_http_tls_cb(BIO *hbio, /* APP_HTTP_TLS_INFO */ void *arg, + int connect, int detail); +# ifndef OPENSSL_NO_SOCK +ASN1_VALUE *app_http_get_asn1(const char *url, const char *proxy, + const char *proxy_port, SSL_CTX *ssl_ctx, + const STACK_OF(CONF_VALUE) *headers, + long timeout, const char *expected_content_type, + const ASN1_ITEM *it); +ASN1_VALUE *app_http_post_asn1(const char *host, const char *port, + const char *path, const char *proxy, + const char *proxy_port, SSL_CTX *ctx, + const STACK_OF(CONF_VALUE) *headers, + const char *content_type, + ASN1_VALUE *req, const ASN1_ITEM *req_it, + long timeout, const ASN1_ITEM *rsp_it); +# endif + # define EXT_COPY_NONE 0 # define EXT_COPY_ADD 1 # define EXT_COPY_ALL 2 diff --git a/apps/lib/apps.c b/apps/lib/apps.c index 3a18cd007c..bf20254463 100644 --- a/apps/lib/apps.c +++ b/apps/lib/apps.c @@ -441,62 +441,14 @@ static int load_pkcs12(BIO *in, const char *desc, return ret; } -#if !defined(OPENSSL_NO_OCSP) && !defined(OPENSSL_NO_SOCK) -static int load_cert_crl_http(const char *url, X509 **pcert, X509_CRL **pcrl) -{ - char *host = NULL, *port = NULL, *path = NULL; - BIO *bio = NULL; - OCSP_REQ_CTX *rctx = NULL; - int use_ssl, rv = 0; - if (!OCSP_parse_url(url, &host, &port, &path, &use_ssl)) - goto err; - if (use_ssl) { - BIO_puts(bio_err, "https not supported\n"); - goto err; - } - bio = BIO_new_connect(host); - if (!bio || !BIO_set_conn_port(bio, port)) - goto err; - rctx = OCSP_REQ_CTX_new(bio, 1024); - if (rctx == NULL) - goto err; - if (!OCSP_REQ_CTX_http(rctx, "GET", path)) - goto err; - if (!OCSP_REQ_CTX_add1_header(rctx, "Host", host)) - goto err; - if (pcert) { - do { - rv = X509_http_nbio(rctx, pcert); - } while (rv == -1); - } else { - do { - rv = X509_CRL_http_nbio(rctx, pcrl); - } while (rv == -1); - } - - err: - OPENSSL_free(host); - OPENSSL_free(path); - OPENSSL_free(port); - BIO_free_all(bio); - OCSP_REQ_CTX_free(rctx); - if (rv != 1) { - BIO_printf(bio_err, "Error loading %s from %s\n", - pcert ? "certificate" : "CRL", url); - ERR_print_errors(bio_err); - } - return rv; -} -#endif - X509 *load_cert(const char *file, int format, const char *cert_descrip) { X509 *x = NULL; BIO *cert; if (format == FORMAT_HTTP) { -#if !defined(OPENSSL_NO_OCSP) && !defined(OPENSSL_NO_SOCK) - load_cert_crl_http(file, &x, NULL); +#if !defined(OPENSSL_NO_SOCK) + x = X509_load_http(file, NULL, NULL, 0 /* timeout */); #endif return x; } @@ -537,8 +489,8 @@ X509_CRL *load_crl(const char *infile, int format) BIO *in = NULL; if (format == FORMAT_HTTP) { -#if !defined(OPENSSL_NO_OCSP) && !defined(OPENSSL_NO_SOCK) - load_cert_crl_http(infile, NULL, &x); +#if !defined(OPENSSL_NO_SOCK) + x = X509_CRL_load_http(infile, NULL, NULL, 0 /* timeout */); #endif return x; } @@ -1981,6 +1933,137 @@ void store_setup_crl_download(X509_STORE *st) X509_STORE_set_lookup_crls_cb(st, crls_http_cb); } +#ifndef OPENSSL_NO_SOCK +static const char *tls_error_hint(void) +{ + unsigned long err = ERR_peek_error(); + + if (ERR_GET_LIB(err) != ERR_LIB_SSL) + err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) != ERR_LIB_SSL) + return NULL; + + switch (ERR_GET_REASON(err)) { + case SSL_R_WRONG_VERSION_NUMBER: + return "The server does not support (a suitable version of) TLS"; + case SSL_R_UNKNOWN_PROTOCOL: + return "The server does not support HTTPS"; + case SSL_R_CERTIFICATE_VERIFY_FAILED: + return "Cannot authenticate server via its TLS certificate, likely due to mismatch with our trusted TLS certs or missing revocation status"; + case SSL_AD_REASON_OFFSET + TLS1_AD_UNKNOWN_CA: + return "Server did not accept our TLS certificate, likely due to mismatch with server's trust anchor or missing revocation status"; + case SSL_AD_REASON_OFFSET + SSL3_AD_HANDSHAKE_FAILURE: + return "TLS handshake failure. Possibly the server requires our TLS certificate but did not receive it"; + default: /* no error or no hint available for error */ + return NULL; + } +} + +/* HTTP callback function that supports TLS connection also via HTTPS proxy */ +BIO *app_http_tls_cb(BIO *hbio, void *arg, int connect, int detail) +{ + APP_HTTP_TLS_INFO *info = (APP_HTTP_TLS_INFO *)arg; + SSL_CTX *ssl_ctx = info->ssl_ctx; + SSL *ssl; + BIO *sbio = NULL; + + if (connect && detail) { /* connecting with TLS */ + if ((info->use_proxy + && !OSSL_HTTP_proxy_connect(hbio, info->server, info->port, + NULL, NULL, /* no proxy credentials */ + info->timeout, bio_err, opt_getprog())) + || (sbio = BIO_new(BIO_f_ssl())) == NULL) { + return NULL; + } + if (ssl_ctx == NULL || (ssl = SSL_new(ssl_ctx)) == NULL) { + BIO_free(sbio); + return NULL; + } + + SSL_set_tlsext_host_name(ssl, info->server); + + SSL_set_connect_state(ssl); + BIO_set_ssl(sbio, ssl, BIO_CLOSE); + + hbio = BIO_push(sbio, hbio); + } else if (!connect && !detail) { /* disconnecting after error */ + const char *hint = tls_error_hint(); + if (hint != NULL) + ERR_add_error_data(1, hint); + /* + * If we pop sbio and BIO_free() it this may lead to libssl double free. + * Rely on BIO_free_all() done by OSSL_HTTP_transfer() in http_client.c + */ + } + return hbio; +} + +ASN1_VALUE *app_http_get_asn1(const char *url, const char *proxy, + const char *proxy_port, SSL_CTX *ssl_ctx, + const STACK_OF(CONF_VALUE) *headers, + long timeout, const char *expected_content_type, + const ASN1_ITEM *it) +{ + APP_HTTP_TLS_INFO info; + char *server; + char *port; + int use_ssl; + ASN1_VALUE *resp = NULL; + + if (url == NULL || it == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + + if (!OSSL_HTTP_parse_url(url, &server, &port, NULL /* ppath */, &use_ssl)) + return NULL; + if (use_ssl && ssl_ctx == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + ERR_add_error_data(1, "missing SSL_CTX"); + goto end; + } + + info.server = server; + info.port = port; + info.use_proxy = proxy != NULL; + info.timeout = timeout; + info.ssl_ctx = ssl_ctx; + resp = OSSL_HTTP_get_asn1(url, proxy, proxy_port, + NULL, NULL, app_http_tls_cb, &info, + headers, 0 /* maxline */, 0 /* max_resp_len */, + timeout, expected_content_type, it); + end: + OPENSSL_free(server); + OPENSSL_free(port); + return resp; + +} + +ASN1_VALUE *app_http_post_asn1(const char *host, const char *port, + const char *path, const char *proxy, + const char *proxy_port, SSL_CTX *ssl_ctx, + const STACK_OF(CONF_VALUE) *headers, + const char *content_type, + ASN1_VALUE *req, const ASN1_ITEM *req_it, + long timeout, const ASN1_ITEM *rsp_it) +{ + APP_HTTP_TLS_INFO info; + + 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, proxy_port, + NULL, NULL, app_http_tls_cb, &info, + headers, content_type, req, req_it, + 0 /* maxline */, + 0 /* max_resp_len */, timeout, NULL, rsp_it); +} + +#endif + /* * Platform-specific sections */ diff --git a/apps/ocsp.c b/apps/ocsp.c index 4c66e966ef..3297b4287a 100644 --- a/apps/ocsp.c +++ b/apps/ocsp.c @@ -118,13 +118,6 @@ static int print_syslog(const char *str, size_t len, void *levPtr); static void socket_timeout(int signum); # endif -# ifndef OPENSSL_NO_SOCK -static OCSP_RESPONSE *query_responder(BIO *cbio, const char *host, - const char *path, - const STACK_OF(CONF_VALUE) *headers, - OCSP_REQUEST *req, int req_timeout); -# endif - typedef enum OPTION_choice { OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, OPT_OUTFILE, OPT_TIMEOUT, OPT_URL, OPT_HOST, OPT_PORT, @@ -315,7 +308,8 @@ int ocsp_main(int argc, char **argv) OPENSSL_free(tport); OPENSSL_free(tpath); thost = tport = tpath = NULL; - if (!OCSP_parse_url(opt_arg(), &host, &port, &path, &use_ssl)) { + if (!OSSL_HTTP_parse_url(opt_arg(), + &host, &port, &path, &use_ssl)) { BIO_printf(bio_err, "%s Error parsing URL\n", prog); goto end; } @@ -1541,133 +1535,34 @@ static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp) } # ifndef OPENSSL_NO_SOCK -static OCSP_RESPONSE *query_responder(BIO *cbio, const char *host, - const char *path, - const STACK_OF(CONF_VALUE) *headers, - OCSP_REQUEST *req, int req_timeout) -{ - int fd; - int rv; - int i; - int add_host = 1; - 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(bio_err, "Error connecting BIO\n"); - return NULL; - } - - if (BIO_get_fd(cbio, &fd) < 0) { - BIO_puts(bio_err, "Can't get connection fd\n"); - goto err; - } - - if (req_timeout != -1 && 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(bio_err, "Timeout on connect\n"); - return NULL; - } - } - - ctx = OCSP_sendreq_new(cbio, path, NULL, -1); - if (ctx == NULL) - return NULL; - - for (i = 0; i < sk_CONF_VALUE_num(headers); i++) { - CONF_VALUE *hdr = sk_CONF_VALUE_value(headers, i); - if (add_host == 1 && strcasecmp("host", hdr->name) == 0) - add_host = 0; - if (!OCSP_REQ_CTX_add1_header(ctx, hdr->name, hdr->value)) - goto err; - } - - if (add_host == 1 && OCSP_REQ_CTX_add1_header(ctx, "Host", host) == 0) - goto err; - - if (!OCSP_REQ_CTX_set1_req(ctx, req)) - goto err; - - for (;;) { - rv = OCSP_sendreq_nbio(&rsp, ctx); - if (rv != -1) - break; - if (req_timeout == -1) - continue; - 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(bio_err, "Unexpected retry condition\n"); - goto err; - } - if (rv == 0) { - BIO_puts(bio_err, "Timeout on request\n"); - break; - } - if (rv == -1) { - BIO_puts(bio_err, "Select error\n"); - break; - } - - } - err: - OCSP_REQ_CTX_free(ctx); - - return rsp; -} - OCSP_RESPONSE *process_responder(OCSP_REQUEST *req, const char *host, const char *path, const char *port, int use_ssl, STACK_OF(CONF_VALUE) *headers, int req_timeout) { - BIO *cbio = NULL; SSL_CTX *ctx = NULL; OCSP_RESPONSE *resp = NULL; - cbio = BIO_new_connect(host); - if (cbio == NULL) { - BIO_printf(bio_err, "Error creating connect BIO\n"); - goto end; - } - if (port != NULL) - BIO_set_conn_port(cbio, port); if (use_ssl == 1) { - BIO *sbio; ctx = SSL_CTX_new(TLS_client_method()); if (ctx == NULL) { BIO_printf(bio_err, "Error creating SSL context.\n"); goto end; } SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); - sbio = BIO_new_ssl(ctx, 1); - cbio = BIO_push(sbio, cbio); } - resp = query_responder(cbio, host, path, headers, req, req_timeout); + resp = (OCSP_RESPONSE *) + app_http_post_asn1(host, port, path, NULL, NULL /* no proxy used */, + ctx, headers, "application/ocsp-request", + (ASN1_VALUE *)req, ASN1_ITEM_rptr(OCSP_REQUEST), + req_timeout, ASN1_ITEM_rptr(OCSP_RESPONSE)); + if (resp == NULL) BIO_printf(bio_err, "Error querying OCSP responder\n"); + end: - BIO_free_all(cbio); SSL_CTX_free(ctx); return resp; } diff --git a/apps/s_client.c b/apps/s_client.c index 87fb80afb7..cb2af7edde 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -75,7 +75,6 @@ static void print_stuff(BIO *berr, SSL *con, int full); static int ocsp_resp_cb(SSL *s, void *arg); #endif static int ldap_ExtendedResponse_parse(const char *buf, long rem); -static char *base64encode (const void *buf, size_t len); static int is_dNS_name(const char *host); static int saved_errno; @@ -949,7 +948,7 @@ int s_client_main(int argc, char **argv) int prexit = 0; int sdebug = 0; int reconnect = 0, verify = SSL_VERIFY_NONE, vpmtouched = 0; - int ret = 1, in_init = 1, i, nbio_test = 0, s = -1, k, width, state = 0; + int ret = 1, in_init = 1, i, nbio_test = 0, sock = -1, k, width, state = 0; int sbuf_len, sbuf_off, cmdletters = 1; int socket_family = AF_UNSPEC, socket_type = SOCK_STREAM, protocol = 0; int starttls_proto = PROTO_OFF, crl_format = FORMAT_PEM, crl_download = 0; @@ -2095,16 +2094,16 @@ int s_client_main(int argc, char **argv) } re_start: - if (init_client(&s, host, port, bindhost, bindport, socket_family, + if (init_client(&sock, host, port, bindhost, bindport, socket_family, socket_type, protocol) == 0) { BIO_printf(bio_err, "connect:errno=%d\n", get_last_socket_error()); - BIO_closesocket(s); + BIO_closesocket(sock); goto end; } - BIO_printf(bio_c_out, "CONNECTED(%08X)\n", s); + BIO_printf(bio_c_out, "CONNECTED(%08X)\n", sock); if (c_nbio) { - if (!BIO_socket_nbio(s, 1)) { + if (!BIO_socket_nbio(sock, 1)) { ERR_print_errors(bio_err); goto end; } @@ -2116,21 +2115,21 @@ int s_client_main(int argc, char **argv) #ifndef OPENSSL_NO_SCTP if (protocol == IPPROTO_SCTP) - sbio = BIO_new_dgram_sctp(s, BIO_NOCLOSE); + sbio = BIO_new_dgram_sctp(sock, BIO_NOCLOSE); else #endif - sbio = BIO_new_dgram(s, BIO_NOCLOSE); + sbio = BIO_new_dgram(sock, BIO_NOCLOSE); if ((peer_info.addr = BIO_ADDR_new()) == NULL) { BIO_printf(bio_err, "memory allocation failure\n"); - BIO_closesocket(s); + BIO_closesocket(sock); goto end; } - if (!BIO_sock_info(s, BIO_SOCK_INFO_ADDRESS, &peer_info)) { + if (!BIO_sock_info(sock, BIO_SOCK_INFO_ADDRESS, &peer_info)) { BIO_printf(bio_err, "getsockname:errno=%d\n", get_last_socket_error()); BIO_ADDR_free(peer_info.addr); - BIO_closesocket(s); + BIO_closesocket(sock); goto end; } @@ -2167,7 +2166,7 @@ int s_client_main(int argc, char **argv) } } else #endif /* OPENSSL_NO_DTLS */ - sbio = BIO_new_socket(s, BIO_NOCLOSE); + sbio = BIO_new_socket(sock, BIO_NOCLOSE); if (nbio_test) { BIO *test; @@ -2398,83 +2397,9 @@ int s_client_main(int argc, char **argv) } break; case PROTO_CONNECT: - { - enum { - error_proto, /* Wrong protocol, not even HTTP */ - error_connect, /* CONNECT failed */ - success - } foundit = error_connect; - BIO *fbio = BIO_new(BIO_f_buffer()); - - BIO_push(fbio, sbio); - BIO_printf(fbio, "CONNECT %s HTTP/1.0\r\n", connectstr); - /* - * Workaround for broken proxies which would otherwise close - * the connection when entering tunnel mode (eg Squid 2.6) - */ - BIO_printf(fbio, "Proxy-Connection: Keep-Alive\r\n"); - - /* Support for basic (base64) proxy authentication */ - if (proxyuser != NULL) { - size_t l; - char *proxyauth, *proxyauthenc; - - l = strlen(proxyuser); - if (proxypass != NULL) - l += strlen(proxypass); - proxyauth = app_malloc(l + 2, "Proxy auth string"); - BIO_snprintf(proxyauth, l + 2, "%s:%s", proxyuser, - (proxypass != NULL) ? proxypass : ""); - proxyauthenc = base64encode(proxyauth, strlen(proxyauth)); - BIO_printf(fbio, "Proxy-Authorization: Basic %s\r\n", - proxyauthenc); - OPENSSL_clear_free(proxyauth, strlen(proxyauth)); - OPENSSL_clear_free(proxyauthenc, strlen(proxyauthenc)); - } - - /* Terminate the HTTP CONNECT request */ - BIO_printf(fbio, "\r\n"); - (void)BIO_flush(fbio); - /* - * The first line is the HTTP response. According to RFC 7230, - * it's formatted exactly like this: - * - * HTTP/d.d ddd Reason text\r\n - */ - mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); - if (mbuf_len < (int)strlen("HTTP/1.0 200")) { - BIO_printf(bio_err, - "%s: HTTP CONNECT failed, insufficient response " - "from proxy (got %d octets)\n", prog, mbuf_len); - (void)BIO_flush(fbio); - BIO_pop(fbio); - BIO_free(fbio); - goto shut; - } - if (mbuf[8] != ' ') { - BIO_printf(bio_err, - "%s: HTTP CONNECT failed, incorrect response " - "from proxy\n", prog); - foundit = error_proto; - } else if (mbuf[9] != '2') { - BIO_printf(bio_err, "%s: HTTP CONNECT failed: %s ", prog, - &mbuf[9]); - } else { - foundit = success; - } - if (foundit != error_proto) { - /* Read past all following headers */ - do { - mbuf_len = BIO_gets(fbio, mbuf, BUFSIZZ); - } while (mbuf_len > 2); - } - (void)BIO_flush(fbio); - BIO_pop(fbio); - BIO_free(fbio); - if (foundit != success) { - goto shut; - } - } + if (!OSSL_HTTP_proxy_connect(sbio, host, port, proxyuser, proxypass, + 0 /* no timeout */, bio_err, prog)) + goto shut; break; case PROTO_IRC: { @@ -3192,8 +3117,8 @@ int s_client_main(int argc, char **argv) timeout.tv_usec = 500000; /* some extreme round-trip */ do { FD_ZERO(&readfds); - openssl_fdset(s, &readfds); - } while (select(s + 1, &readfds, NULL, NULL, &timeout) > 0 + openssl_fdset(sock, &readfds); + } while (select(sock + 1, &readfds, NULL, NULL, &timeout) > 0 && BIO_read(sbio, sbuf, BUFSIZZ) > 0); BIO_closesocket(SSL_get_fd(con)); @@ -3571,29 +3496,6 @@ static int ldap_ExtendedResponse_parse(const char *buf, long rem) } /* - * BASE64 encoder: used only for encoding basic proxy authentication credentials - */ -static char *base64encode (const void *buf, size_t len) -{ - int i; - size_t outl; - char *out; - - /* Calculate size of encoded data */ - outl = (len / 3); - if (len % 3 > 0) - outl++; - outl <<= 2; - out = app_malloc(outl + 1, "base64 encode buffer"); - - i = EVP_EncodeBlock((unsigned char *)out, buf, len); - assert(i <= (int)outl); - if (i < 0) - *out = '\0'; - return out; -} - -/* * Host dNS Name verifier: used for checking that the hostname is in dNS format * before setting it as SNI */ diff --git a/apps/s_server.c b/apps/s_server.c index c81e572267..69d9e04876 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -526,8 +526,8 @@ static int get_ocsp_resp_from_responder(SSL *s, tlsextstatusctx *srctx, x = SSL_get_certificate(s); aia = X509_get1_ocsp(x); if (aia != NULL) { - if (!OCSP_parse_url(sk_OPENSSL_STRING_value(aia, 0), - &host, &port, &path, &use_ssl)) { + if (!OSSL_HTTP_parse_url(sk_OPENSSL_STRING_value(aia, 0), + &host, &port, &path, &use_ssl)) { BIO_puts(bio_err, "cert_status: can't parse AIA URL\n"); goto err; } @@ -1387,10 +1387,9 @@ int s_server_main(int argc, char *argv[]) case OPT_STATUS_URL: #ifndef OPENSSL_NO_OCSP s_tlsextstatus = 1; - if (!OCSP_parse_url(opt_arg(), - &tlscstatp.host, - &tlscstatp.port, - &tlscstatp.path, &tlscstatp.use_ssl)) { + if (!OSSL_HTTP_parse_url(opt_arg(), + &tlscstatp.host, &tlscstatp.port, + &tlscstatp.path, &tlscstatp.use_ssl)) { BIO_printf(bio_err, "Error parsing URL\n"); goto end; } @@ -3545,7 +3544,7 @@ static int generate_session_id(SSL *ssl, unsigned char *id, { unsigned int count = 0; unsigned int session_id_prefix_len = strlen(session_id_prefix); - + do { if (RAND_bytes(id, *id_len) <= 0) return 0; diff --git a/crypto/build.info b/crypto/build.info index 6906c54db2..b21cf3f45a 100644 --- a/crypto/build.info +++ b/crypto/build.info @@ -5,7 +5,7 @@ SUBDIRS=objects buffer bio stack lhash rand evp asn1 pem x509 conf \ md2 md4 md5 sha mdc2 hmac ripemd whrlpool poly1305 \ siphash sm3 des aes rc2 rc4 rc5 idea aria bf cast camellia \ seed sm4 chacha modes bn ec rsa dsa dh sm2 dso engine \ - err comp ocsp cms ts srp cmac ct async ess crmf cmp serializer \ + err comp http ocsp cms ts srp cmac ct async ess crmf cmp serializer \ ffc LIBS=../libcrypto diff --git a/crypto/cmp/cmp_ctx.c b/crypto/cmp/cmp_ctx.c index 0bd12f47ac..fc89ea6bc8 100644 --- a/crypto/cmp/cmp_ctx.c +++ b/crypto/cmp/cmp_ctx.c @@ -819,7 +819,7 @@ int OSSL_CMP_CTX_set_proxyPort(OSSL_CMP_CTX *ctx, int port) * sets the http connect/disconnect callback function to be used for HTTP(S) * returns 1 on success, 0 on error */ -int OSSL_CMP_CTX_set_http_cb(OSSL_CMP_CTX *ctx, OSSL_cmp_http_cb_t cb) +int OSSL_CMP_CTX_set_http_cb(OSSL_CMP_CTX *ctx, OSSL_HTTP_bio_cb_t cb) { if (ctx == NULL) { CMPerr(0, CMP_R_NULL_ARGUMENT); diff --git a/crypto/cmp/cmp_err.c b/crypto/cmp/cmp_err.c index 8b4a6ca708..a6d59f9fc4 100644 --- a/crypto/cmp/cmp_err.c +++ b/crypto/cmp/cmp_err.c @@ -67,6 +67,8 @@ static const ERR_STRING_DATA CMP_str_reasons[] = { {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_NULL_ARGUMENT), "null argument"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_PKISTATUSINFO_NOT_FOUND), "pkistatusinfo not found"}, + {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_POTENTIALLY_INVALID_CERTIFICATE), + "potentially invalid certificate"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNEXPECTED_PKIBODY), "unexpected pkibody"}, {ERR_PACK(ERR_LIB_CMP, 0, CMP_R_UNKNOWN_ALGORITHM_ID), "unknown algorithm id"}, diff --git a/crypto/cmp/cmp_local.h b/crypto/cmp/cmp_local.h index b7ab6454b5..f705cb24be 100644 --- a/crypto/cmp/cmp_local.h +++ b/crypto/cmp/cmp_local.h @@ -44,7 +44,7 @@ struct ossl_cmp_ctx_st { int totaltimeout; /* maximum number seconds an enrollment may take, incl. */ /* attempts polling for a response if a 'waiting' PKIStatus is received */ time_t end_time; /* session start time + totaltimeout */ - OSSL_cmp_http_cb_t http_cb; + OSSL_HTTP_bio_cb_t http_cb; void *http_cb_arg; /* allows to store optional argument to cb */ /* server authentication */ diff --git a/crypto/err/err.c b/crypto/err/err.c index e77cfe83cf..efc6273350 100644 --- a/crypto/err/err.c +++ b/crypto/err/err.c @@ -76,6 +76,7 @@ static ERR_STRING_DATA ERR_str_libraries[] = { {ERR_PACK(ERR_LIB_ESS, 0, 0), "ESS routines"}, {ERR_PACK(ERR_LIB_PROV, 0, 0), "Provider routines"}, {ERR_PACK(ERR_LIB_OSSL_SERIALIZER, 0, 0), "SERIALIZER routines"}, + {ERR_PACK(ERR_LIB_HTTP, 0, 0), "HTTP routines"}, {0, NULL}, }; diff --git a/crypto/err/err_all.c b/crypto/err/err_all.c index 13bef4a7a8..49d4e3616d 100644 --- a/crypto/err/err_all.c +++ b/crypto/err/err_all.c @@ -30,6 +30,7 @@ #include "internal/dso.h" #include <openssl/engineerr.h> #include <openssl/uierr.h> +#include <openssl/httperr.h> #include <openssl/ocsperr.h> #include <openssl/err.h> #include <openssl/tserr.h> @@ -85,6 +86,7 @@ int err_load_crypto_strings_int(void) # ifndef OPENSSL_NO_ENGINE ERR_load_ENGINE_strings() == 0 || # endif + ERR_load_HTTP_strings() == 0 || # ifndef OPENSSL_NO_OCSP ERR_load_OCSP_strings() == 0 || # endif diff --git a/crypto/err/openssl.ec b/crypto/err/openssl.ec index 485c0c89ce..1ec7bb1162 100644 --- a/crypto/err/openssl.ec +++ b/crypto/err/openssl.ec @@ -41,6 +41,7 @@ L ESS include/openssl/ess.h crypto/ess/ess_err.c L PROP include/internal/property.h crypto/property/property_err.c L PROV providers/common/include/prov/providercommon.h providers/common/provider_err.c L OSSL_SERIALIZER include/openssl/serializer.h crypto/serializer/serializer_err.c +L HTTP include/openssl/http.h crypto/http/http_err.c # additional header files to be scanned for function names L NONE include/openssl/x509_vfy.h NONE diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 84a8adc52c..a663bd2858 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -940,11 +940,9 @@ OCSP_F_OCSP_CHECK_IDS:107:ocsp_check_ids OCSP_F_OCSP_CHECK_ISSUER:108:ocsp_check_issuer OCSP_F_OCSP_CHECK_VALIDITY:115:OCSP_check_validity OCSP_F_OCSP_MATCH_ISSUERID:109:ocsp_match_issuerid -OCSP_F_OCSP_PARSE_URL:114:OCSP_parse_url OCSP_F_OCSP_REQUEST_SIGN:110:OCSP_request_sign OCSP_F_OCSP_REQUEST_VERIFY:116:OCSP_request_verify OCSP_F_OCSP_RESPONSE_GET1_BASIC:111:OCSP_response_get1_basic -OCSP_F_PARSE_HTTP_LINE1:118:parse_http_line1 OSSL_STORE_F_FILE_CTRL:129:file_ctrl OSSL_STORE_F_FILE_FIND:138:file_find OSSL_STORE_F_FILE_GET_PASS:118:file_get_pass @@ -2100,6 +2098,7 @@ CMP_R_MULTIPLE_SAN_SOURCES:102:multiple san sources CMP_R_NO_STDIO:194:no stdio CMP_R_NULL_ARGUMENT:103:null argument CMP_R_PKISTATUSINFO_NOT_FOUND:132:pkistatusinfo not found +CMP_R_POTENTIALLY_INVALID_CERTIFICATE:139:potentially invalid certificate CMP_R_UNEXPECTED_PKIBODY:133:unexpected pkibody CMP_R_UNKNOWN_ALGORITHM_ID:134:unknown algorithm id CMP_R_UNKNOWN_CERT_TYPE:135:unknown cert type @@ -2527,6 +2526,28 @@ EVP_R_WRAP_MODE_NOT_ALLOWED:170:wrap mode not allowed EVP_R_WRONG_FINAL_BLOCK_LENGTH:109:wrong final block length EVP_R_XTS_DATA_UNIT_IS_TOO_LARGE:191:xts data unit is too large EVP_R_XTS_DUPLICATED_KEYS:192:xts duplicated keys +HTTP_R_ASN1_LEN_EXCEEDS_MAX_RESP_LEN:108:asn1 len exceeds max resp len +HTTP_R_CONNECT_FAILURE:100:connect failure +HTTP_R_ERROR_PARSING_ASN1_LENGTH:109:error parsing asn1 length +HTTP_R_ERROR_PARSING_CONTENT_LENGTH:119:error parsing content length +HTTP_R_ERROR_PARSING_URL:101:error parsing url +HTTP_R_ERROR_RECEIVING:103:error receiving +HTTP_R_ERROR_SENDING:102:error sending +HTTP_R_INCONSISTENT_CONTENT_LENGTH:120:inconsistent content length +HTTP_R_MAX_RESP_LEN_EXCEEDED:117:max resp len exceeded +HTTP_R_MISSING_ASN1_ENCODING:110:missing asn1 encoding +HTTP_R_MISSING_CONTENT_TYPE:121:missing content type +HTTP_R_MISSING_REDIRECT_LOCATION:111:missing redirect location +HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP:112:redirection from https to http +HTTP_R_REDIRECTION_NOT_ENABLED:116:redirection not enabled +HTTP_R_RESPONSE_LINE_TOO_LONG:113:response line too long +HTTP_R_SERVER_RESPONSE_PARSE_ERROR:104:server response parse error +HTTP_R_SERVER_SENT_ERROR:105:server sent error +HTTP_R_SERVER_SENT_WRONG_HTTP_VERSION:106:server sent wrong http version +HTTP_R_STATUS_CODE_UNSUPPORTED:114:status code unsupported +HTTP_R_TLS_NOT_ENABLED:107:tls not enabled +HTTP_R_TOO_MANY_REDIRECTIONS:115:too many redirections +HTTP_R_UNEXPECTED_CONTENT_TYPE:118:unexpected content type KDF_R_BAD_ENCODING:122:bad encoding KDF_R_BAD_LENGTH:123:bad length KDF_R_BOTH_MODE_AND_MODE_INT:127:both mode and mode int @@ -2561,7 +2582,6 @@ OCSP_R_CERTIFICATE_VERIFY_ERROR:101:certificate verify error OCSP_R_DIGEST_ERR:102:digest err OCSP_R_ERROR_IN_NEXTUPDATE_FIELD:122:error in nextupdate field OCSP_R_ERROR_IN_THISUPDATE_FIELD:123:error in thisupdate field -OCSP_R_ERROR_PARSING_URL:121:error parsing url OCSP_R_MISSING_OCSPSIGNING_USAGE:103:missing ocspsigning usage OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE:124:nextupdate before thisupdate OCSP_R_NOT_BASIC_RESPONSE:104:not basic response @@ -2575,8 +2595,6 @@ OCSP_R_REQUEST_NOT_SIGNED:128:request not signed OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA:111:\ response contains no revocation data OCSP_R_ROOT_CA_NOT_TRUSTED:112:root ca not trusted -OCSP_R_SERVER_RESPONSE_ERROR:114:server response error -OCSP_R_SERVER_RESPONSE_PARSE_ERROR:115:server response parse error OCSP_R_SIGNATURE_FAILURE:117:signature failure OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND:118:signer certificate not found OCSP_R_STATUS_EXPIRED:125:status expired diff --git a/crypto/http/build.info b/crypto/http/build.info new file mode 100644 index 0000000000..b4626b13de --- /dev/null +++ b/crypto/http/build.info @@ -0,0 +1,2 @@ +LIBS=../../libcrypto +SOURCE[../../libcrypto]=http_client.c http_err.c http_lib.c diff --git a/crypto/http/http_client.c b/crypto/http/http_client.c new file mode 100644 index 0000000000..424b4c3922 --- /dev/null +++ b/crypto/http/http_client.c @@ -0,0 +1,1238 @@ +/* + * Copyright 2001-2020 The OpenSSL Project Authors. All Rights Reserved. + * Copyright Siemens AG 2018-2020 + * + * 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 + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include "e_os.h" +#include <stdio.h> +#include <stdlib.h> +#include "crypto/ctype.h" +#include <string.h> +#include <openssl/asn1.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/httperr.h> +#include <openssl/cmperr.h> +#include <openssl/buffer.h> +#include <openssl/http.h> +#include "internal/sockets.h" +#include "internal/cryptlib.h" + +#include "http_local.h" + +#define HTTP_PREFIX "HTTP/" +#define HTTP_VERSION_PATT "1." /* allow 1.x */ +#define HTTP_VERSION_STR_LEN 3 +#define HTTP_LINE1_MINLEN ((int)strlen(HTTP_PREFIX HTTP_VERSION_PATT "x 200\n")) +#define HTTP_VERSION_MAX_REDIRECTIONS 50 + +#define HTTP_STATUS_CODE_OK 200 +#define HTTP_STATUS_CODE_MOVED_PERMANENTLY 301 +#define HTTP_STATUS_CODE_FOUND 302 + + +/* Stateful HTTP request code, supporting blocking and non-blocking I/O */ + +/* Opaque HTTP request status structure */ + +struct ossl_http_req_ctx_st { + int state; /* Current I/O state */ + unsigned char *iobuf; /* Line buffer */ + int iobuflen; /* Line buffer length */ + BIO *wbio; /* BIO to send request to */ + BIO *rbio; /* BIO to read response from */ + BIO *mem; /* Memory BIO response is built into */ + int method_GET; /* HTTP method "GET" or "POST" */ + const char *expected_ct; /* expected Content-Type, or NULL */ + int expect_asn1; /* response must be ASN.1-encoded */ + 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 */ + char *redirection_url; /* Location given with HTTP status 301/302 */ +}; + +#define HTTP_DEFAULT_MAX_LINE_LENGTH (4 * 1024) +#define HTTP_DEFAULT_MAX_RESP_LEN (100 * 1024) + +/* HTTP states */ + +#define OHS_NOREAD 0x1000 /* If set no reading should be performed */ +#define OHS_ERROR (0 | OHS_NOREAD) /* Error condition */ +#define OHS_FIRSTLINE 1 /* First line being read */ +#define OHS_REDIRECT 0xa /* Looking for redirection location */ +#define OHS_HEADERS 2 /* MIME headers being read */ +#define OHS_ASN1_HEADER 3 /* HTTP initial header (tag+length) being read */ +#define OHS_CONTENT 4 /* HTTP content octets being read */ +#define OHS_WRITE_INIT (5 | OHS_NOREAD) /* 1st call: ready to start I/O */ +#define OHS_WRITE (6 | OHS_NOREAD) /* Request being sent */ +#define OHS_FLUSH (7 | OHS_NOREAD) /* Request being flushed */ +#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 method_GET, int maxline, + unsigned long max_resp_len, + int timeout, + const char *expected_content_type, + int expect_asn1) +{ + OSSL_HTTP_REQ_CTX *rctx; + + if (wbio == NULL || rbio == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + + if ((rctx = OPENSSL_zalloc(sizeof(*rctx))) == NULL) + return NULL; + rctx->state = OHS_ERROR; + rctx->iobuflen = maxline > 0 ? maxline : HTTP_DEFAULT_MAX_LINE_LENGTH; + rctx->iobuf = OPENSSL_malloc(rctx->iobuflen); + rctx->wbio = wbio; + rctx->rbio = rbio; + rctx->mem = BIO_new(BIO_s_mem()); + if (rctx->iobuf == NULL || rctx->mem == NULL) { + OSSL_HTTP_REQ_CTX_free(rctx); + return NULL; + } + rctx->method_GET = method_GET; + rctx->expected_ct = expected_content_type; + 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; + return rctx; +} + +void OSSL_HTTP_REQ_CTX_free(OSSL_HTTP_REQ_CTX *rctx) +{ + if (rctx == NULL) + return; + BIO_free(rctx->mem); /* this may indirectly call ERR_clear_error() */ + OPENSSL_free(rctx->iobuf); + OPENSSL_free(rctx); +} + +BIO *OSSL_HTTP_REQ_CTX_get0_mem_bio(OSSL_HTTP_REQ_CTX *rctx) +{ + if (rctx == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + return rctx->mem; +} + +void OSSL_HTTP_REQ_CTX_set_max_response_length(OSSL_HTTP_REQ_CTX *rctx, + unsigned long len) +{ + if (rctx == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return; + } + rctx->max_resp_len = len != 0 ? len : HTTP_DEFAULT_MAX_RESP_LEN; +} + +/* + * Create HTTP header using given op and path (or "/" in case path is NULL). + * Server name (and port) must be given if and only if plain HTTP proxy is used. + */ +int OSSL_HTTP_REQ_CTX_header(OSSL_HTTP_REQ_CTX *rctx, const char *server, + const char *port, const char *path) +{ + if (rctx == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (BIO_printf(rctx->mem, "%s ", rctx->method_GET ? "GET" : "POST") <= 0) + return 0; + + if (server != NULL) { /* HTTP (but not HTTPS) proxy is used */ + /* + * Section 5.1.2 of RFC 1945 states that the absoluteURI form is only + * allowed when using a proxy + */ + if (BIO_printf(rctx->mem, "http://%s", server) <= 0) + return 0; + if (port != NULL && BIO_printf(rctx->mem, ":%s", port) <= 0) + return 0; + } + + /* Make sure path includes a forward slash */ + if (path == NULL) + path = "/"; + if (path[0] != '/' && BIO_printf(rctx->mem, "/") <= 0) + return 0; + + if (BIO_printf(rctx->mem, "%s "HTTP_PREFIX"1.0\r\n", path) <= 0) + return 0; + rctx->state = OHS_HTTP_HEADER; + return 1; +} + +int OSSL_HTTP_REQ_CTX_add1_header(OSSL_HTTP_REQ_CTX *rctx, + const char *name, const char *value) +{ + if (rctx == NULL || name == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (BIO_puts(rctx->mem, name) <= 0) + return 0; + if (value != NULL) { + if (BIO_write(rctx->mem, ": ", 2) != 2) + return 0; + if (BIO_puts(rctx->mem, value) <= 0) + return 0; + } + if (BIO_write(rctx->mem, "\r\n", 2) != 2) + return 0; + rctx->state = OHS_HTTP_HEADER; + return 1; +} + +static int OSSL_HTTP_REQ_CTX_content(OSSL_HTTP_REQ_CTX *rctx, + const char *content_type, BIO *req_mem) +{ + const unsigned char *req; + long req_len; + + if (rctx == NULL || req_mem == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (content_type != NULL + && BIO_printf(rctx->mem, "Content-Type: %s\r\n", content_type) <= 0) + return 0; + + if ((req_len = BIO_get_mem_data(req_mem, &req)) <= 0) + return 0; + rctx->state = OHS_WRITE_INIT; + + return BIO_printf(rctx->mem, "Content-Length: %ld\r\n\r\n", req_len) > 0 + && BIO_write(rctx->mem, req, req_len) == (int)req_len; +} + +BIO *HTTP_asn1_item2bio(const ASN1_ITEM *it, ASN1_VALUE *val) +{ + BIO *res; + + if (it == NULL || val == NULL) { + HTTPerr(0, 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_i2d(OSSL_HTTP_REQ_CTX *rctx, const char *content_type, + const ASN1_ITEM *it, ASN1_VALUE *req) +{ + BIO *mem; + int res; + + if (rctx == NULL || it == NULL || req == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + res = (mem = HTTP_asn1_item2bio(it, req)) != NULL + && OSSL_HTTP_REQ_CTX_content(rctx, content_type, mem); + BIO_free(mem); + return res; +} + +static int OSSL_HTTP_REQ_CTX_add1_headers(OSSL_HTTP_REQ_CTX *rctx, + const STACK_OF(CONF_VALUE) *headers, + const char *host) +{ + int i; + int add_host = 1; + CONF_VALUE *hdr; + + for (i = 0; i < sk_CONF_VALUE_num(headers); i++) { + hdr = sk_CONF_VALUE_value(headers, i); + if (add_host && strcasecmp("host", hdr->name) == 0) + add_host = 0; + if (!OSSL_HTTP_REQ_CTX_add1_header(rctx, hdr->name, hdr->value)) + return 0; + } + + if (add_host && !OSSL_HTTP_REQ_CTX_add1_header(rctx, "Host", host)) + return 0; + return 1; +} + +/*- + * Create OSSL_HTTP_REQ_CTX structure using the values provided. + * 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 *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) +{ + OSSL_HTTP_REQ_CTX *rctx; + + if (use_http_proxy && (server == NULL || port == NULL)) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + /* remaining parameters are checked indirectly by the functions called */ + + if ((rctx = OSSL_HTTP_REQ_CTX_new(wbio, rbio, req_mem == NULL, maxline, + max_resp_len, timeout, + expected_content_type, expect_asn1)) + == NULL) + return NULL; + + if (OSSL_HTTP_REQ_CTX_header(rctx, use_http_proxy ? server : NULL, + port, path) + && OSSL_HTTP_REQ_CTX_add1_headers(rctx, headers, server) + && (req_mem == NULL + || OSSL_HTTP_REQ_CTX_content(rctx, content_type, req_mem))) + return rctx; + + OSSL_HTTP_REQ_CTX_free(rctx); + return NULL; +} + +/* + * Parse first HTTP response line. This should be 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 *code, *reason, *end; + + /* Skip to first whitespace (past protocol info) */ + for (code = line; *code != '\0' && !ossl_isspace(*code); code++) + continue; + if (*code == '\0') { + HTTPerr(0, HTTP_R_SERVER_RESPONSE_PARSE_ERROR); + return 0; + } + + /* Skip past whitespace to start of response code */ + while (*code != '\0' && ossl_isspace(*code)) + code++; + + if (*code == '\0') { + HTTPerr(0, HTTP_R_SERVER_RESPONSE_PARSE_ERROR); + return 0; + } + + /* Find end of response code: first whitespace after start of code */ + for (reason = code; *reason != '\0' && !ossl_isspace(*reason); reason++) + continue; + + if (*reason == '\0') { + HTTPerr(0, HTTP_R_SERVER_RESPONSE_PARSE_ERROR); + return 0; + } + + /* Set end of response code and start of message */ + *reason++ = '\0'; + + /* Attempt to parse numeric code */ + retcode = strtoul(code, &end, 10); + + if (*end != '\0') + return 0; + + /* Skip over any leading whitespace in message */ + while (*reason != '\0' && ossl_isspace(*reason)) + reason++; + + if (*reason != '\0') { + /* + * Finally zap any trailing whitespace in message (include CRLF) + */ + + /* chop any trailing whitespace from reason */ + /* We know reason has a non-whitespace character so this is OK */ + for (end = reason + strlen(reason) - 1; ossl_isspace(*end); end--) + *end = '\0'; + } + + switch (retcode) { + case HTTP_STATUS_CODE_OK: + case HTTP_STATUS_CODE_MOVED_PERMANENTLY: + case HTTP_STATUS_CODE_FOUND: + return retcode; + default: + if (retcode < 400) + HTTPerr(0, HTTP_R_STATUS_CODE_UNSUPPORTED); + else + HTTPerr(0, HTTP_R_SERVER_SENT_ERROR); + if (*reason == '\0') + ERR_add_error_data(2, "Code=", code); + else + ERR_add_error_data(4, "Code=", code, ",Reason=", reason); + return 0; + } +} + +static int check_set_resp_len(OSSL_HTTP_REQ_CTX *rctx, unsigned long len) +{ + const char *tag = NULL; + unsigned long val = 0; + + if (len > rctx->max_resp_len) { + HTTPerr(0, HTTP_R_MAX_RESP_LEN_EXCEEDED); + tag = ",max="; + val = rctx->max_resp_len; + } + if (rctx->resp_len != 0 && rctx->resp_len != len) { + HTTPerr(0, HTTP_R_INCONSISTENT_CONTENT_LENGTH); + tag = ",before="; + val = rctx->resp_len; + } + if (tag != NULL) { + char len_str[32]; + char str[32]; + + BIO_snprintf(len_str, sizeof(len_str), "%lu", len); + BIO_snprintf(str, sizeof(str), "%lu", val); + ERR_add_error_data(4, "length=", len_str, tag, str); + return 0; + } + rctx->resp_len = len; + return 1; +} + +/* + * Try exchanging request and response via HTTP on (non-)blocking BIO in rctx. + * Returns 1 on success, 0 on error or redirection, -1 on BIO_should_retry. + */ +int OSSL_HTTP_REQ_CTX_nbio(OSSL_HTTP_REQ_CTX *rctx) +{ + int i; + long n, n_to_send = 0; + unsigned long resp_len; + const unsigned char *p; + char *key, *value, *line_end = NULL; + + if (rctx == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + rctx->redirection_url = NULL; + next_io: + if ((rctx->state & OHS_NOREAD) == 0) { + n = BIO_read(rctx->rbio, rctx->iobuf, rctx->iobuflen); + if (n <= 0) { + if (BIO_should_retry(rctx->rbio)) + 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_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_WRITE_INIT; + + /* fall thru */ + case OHS_WRITE_INIT: + n_to_send = BIO_get_mem_data(rctx->mem, NULL); + rctx->state = OHS_WRITE; + + /* fall thru */ + case OHS_WRITE: + n = BIO_get_mem_data(rctx->mem, &p); + + i = BIO_write(rctx->wbio, p + (n - n_to_send), n_to_send); + + if (i <= 0) { + if (BIO_should_retry(rctx->wbio)) + return -1; + rctx->state = OHS_ERROR; + return 0; + } + + n_to_send -= i; + + if (n_to_send > 0) + goto next_io; + + rctx->state = OHS_FLUSH; + + (void)BIO_reset(rctx->mem); + + /* fall thru */ + case OHS_FLUSH: + + i = BIO_flush(rctx->wbio); + + if (i > 0) { + rctx->state = OHS_FIRSTLINE; + goto next_io; + } + + if (BIO_should_retry(rctx->wbio)) + return -1; + + rctx->state = OHS_ERROR; + return 0; + + case OHS_ERROR: + return 0; + + case OHS_FIRSTLINE: + case OHS_HEADERS: + case OHS_REDIRECT: + + /* Attempt to read a line in */ + next_line: + /* + * Due to strange memory BIO behavior 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) == 0) { + 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) { + HTTPerr(0, HTTP_R_RESPONSE_LINE_TOO_LONG); + rctx->state = OHS_ERROR; + return 0; + } + + /* First line */ + if (rctx->state == OHS_FIRSTLINE) { + switch (parse_http_line1((char *)rctx->iobuf)) { + case HTTP_STATUS_CODE_OK: + rctx->state = OHS_HEADERS; + goto next_line; + case HTTP_STATUS_CODE_MOVED_PERMANENTLY: + case HTTP_STATUS_CODE_FOUND: /* i.e., moved temporarily */ + if (rctx->method_GET) { + rctx->state = OHS_REDIRECT; + goto next_line; + } + HTTPerr(0, HTTP_R_REDIRECTION_NOT_ENABLED); + /* redirection is not supported/recommended for POST */ + /* fall through */ + default: + rctx->state = OHS_ERROR; + return 0; + } + } + key = (char *)rctx->iobuf; + value = strchr(key, ':'); + if (value != NULL) { + *(value++) = '\0'; + while (ossl_isspace(*value)) + value++; + line_end = strchr(value, '\r'); + if (line_end == NULL) + line_end = strchr(value, '\n'); + if (line_end != NULL) + *line_end = '\0'; + } + if (value != NULL && line_end != NULL) { + if (rctx->state == OHS_REDIRECT && strcmp(key, "Location") == 0) { + rctx->redirection_url = value; + return 0; + } + if (rctx->expected_ct != NULL && strcmp(key, "Content-Type") == 0) { + if (strcmp(rctx->expected_ct, value) != 0) { + HTTPerr(0, HTTP_R_UNEXPECTED_CONTENT_TYPE); + ERR_add_error_data(4, "expected=", rctx->expected_ct, + ",actual=", value); + return 0; + } + rctx->expected_ct = NULL; /* content-type has been found */ + } + if (strcmp(key, "Content-Length") == 0) { + resp_len = strtoul(value, &line_end, 10); + if (line_end == value || *line_end != '\0') { + HTTPerr(0, HTTP_R_ERROR_PARSING_CONTENT_LENGTH); + ERR_add_error_data(2, "input=", value); + return 0; + } + if (!check_set_resp_len(rctx, resp_len)) + return 0; + } + } + + /* Look for blank line: end of headers */ + for (p = rctx->iobuf; *p != '\0' ; p++) { + if (*p != '\r' && *p != '\n') + break; + } + if (*p != '\0') /* not end of headers */ + goto next_line; + + if (rctx->expected_ct != NULL) { + HTTPerr(0, HTTP_R_MISSING_CONTENT_TYPE); + ERR_add_error_data(2, "expected=", rctx->expected_ct); + return 0; + } + if (rctx->state == OHS_REDIRECT) { + /* http status code indicated redirect but there was no Location */ + HTTPerr(0, HTTP_R_MISSING_REDIRECT_LOCATION); + return 0; + } + + if (!rctx->expect_asn1) { + rctx->state = OHS_CONTENT; + goto content; + } + + rctx->state = OHS_ASN1_HEADER; + + /* Fall thru */ + case OHS_ASN1_HEADER: + /* + * Now reading ASN1 header: can read at least 2 bytes which is enough + * for ASN1 SEQUENCE header and either length field or at least the + * length of the length field. + */ + n = BIO_get_mem_data(rctx->mem, &p); + if (n < 2) + goto next_io; + + /* Check it is an ASN1 SEQUENCE */ + if (*p++ != (V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED)) { + HTTPerr(0, HTTP_R_MISSING_ASN1_ENCODING); + return 0; + } + + /* Check out length field */ + if ((*p & 0x80) != 0) { + /* + * If MSB set on initial length octet we can now always read 6 + * octets: make sure we have them. + */ + if (n < 6) + goto next_io; + n = *p & 0x7F; + /* Not NDEF or excessive length */ + if (n == 0 || (n > 4)) { + HTTPerr(0, HTTP_R_ERROR_PARSING_ASN1_LENGTH); + return 0; + } + p++; + resp_len = 0; + for (i = 0; i < n; i++) { + resp_len <<= 8; + resp_len |= *p++; + } + resp_len += n + 2; + } else { + resp_len = *p + 2; + } + if (!check_set_resp_len(rctx, resp_len)) + return 0; + + content: + rctx->state = OHS_CONTENT; + + /* Fall thru */ + case OHS_CONTENT: + default: + n = BIO_get_mem_data(rctx->mem, NULL); + if (n < (long)rctx->resp_len /* may be 0 if no Content-Type or ASN.1 */) + goto next_io; + + rctx->state = OHS_DONE; + return 1; + } +} + +#ifndef OPENSSL_NO_SOCK + +/* set up a new connection BIO, to HTTP server or to HTTP(S) proxy if given */ +static BIO *HTTP_new_bio(const char *server, const char *server_port, + const char *proxy, const char *proxy_port) +{ + const char *host = server; + const char *port = server_port; + BIO *cbio; + + if (server == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + + if (proxy != NULL) { + host = proxy; + port = proxy_port; + } + cbio = BIO_new_connect(host); + if (cbio == NULL) + goto end; + if (port != NULL) + (void)BIO_set_conn_port(cbio, port); + + end: + return cbio; +} + +static ASN1_VALUE *BIO_mem_d2i(BIO *mem, const ASN1_ITEM *it) +{ + const unsigned char *p; + long len = BIO_get_mem_data(mem, &p); + ASN1_VALUE *resp = ASN1_item_d2i(NULL, &p, len, it); + + if (resp == NULL) + HTTPerr(0, HTTP_R_SERVER_RESPONSE_PARSE_ERROR); + return resp; +} + +static BIO *OSSL_HTTP_REQ_CTX_transfer(OSSL_HTTP_REQ_CTX *rctx) +{ + int sending = 1; + int rv; + + if (rctx == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + + for (;;) { + rv = OSSL_HTTP_REQ_CTX_nbio(rctx); + if (rv != -1) + break; + /* BIO_should_retry was true */ + sending = 0; + /* will not actually wait if rctx->max_time == 0 */ + if (BIO_wait(rctx->rbio, rctx->max_time) <= 0) + return NULL; + } + + if (rv == 0) { + if (rctx->redirection_url == NULL) { /* an error occurred */ + if (sending && (rctx->state & OHS_NOREAD) != 0) + HTTPerr(0, HTTP_R_ERROR_SENDING); + else + HTTPerr(0, HTTP_R_ERROR_RECEIVING); + } + return NULL; + } + if (!BIO_up_ref(rctx->mem)) + return NULL; + 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) { + HTTPerr(0, 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; + + if (timeout == 0) + return 0; + elapsed_time = (long)(time(NULL) - start_time); /* this might overflow */ + return timeout <= elapsed_time ? -1 : timeout - elapsed_time; +} + +/*- + * Exchange HTTP request and response with the given server. + * If req_mem == NULL then use GET and ignore content_type, else POST. + * The redirection_url output (freed by caller) parameter is used only for GET. + * + * Typically the bio and rbio parameters are NULL and a network BIO is created + * internally for connecting to the given server and port, optionally via a + * proxy and its port, and is then used for exchanging the request and response. + * If bio is given and rbio is NULL then this BIO is used instead. + * If both bio and rbio are given (which may be memory BIOs for instance) + * then no explicit connection is attempted, + * bio is used for writing the request, and rbio for reading the response. + * + * bio_update_fn is an optional BIO connect/disconnect callback function, + * which has the prototype + * BIO *(*OSSL_HTTP_bio_cb_t) (BIO *bio, void *arg, int conn, int detail); + * The callback may modify the HTTP BIO provided in the bio argument, + * whereby it may make use of any custom defined argument 'arg'. + * During connection establishment, just after BIO_connect_retry(), + * the callback function is invoked with the 'conn' argument being 1 + * 'detail' indicating whether a HTTPS (i.e., TLS) connection is requested. + * On disconnect 'conn' is 0 and 'detail' indicates that no error occurred. + * For instance, on connect the funct may prepend a TLS BIO to implement HTTPS; + * after disconnect it may do some error diagnostics and/or specific cleanup. + * 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 use_ssl, const char *proxy, const char *proxy_port, + 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) +{ + 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; + BIO *resp = NULL; + + if (redirection_url != NULL) + *redirection_url = NULL; /* do this beforehand to prevent dbl free */ + + if (use_ssl && bio_update_fn == NULL) { + HTTPerr(0, HTTP_R_TLS_NOT_ENABLED); + return NULL; + } + if (rbio != NULL && (bio == NULL || bio_update_fn != NULL)) { + HTTPerr(0, ERR_R_PASSED_INVALID_ARGUMENT); + return NULL; + } + /* remaining parameters are checked indirectly by the functions called */ + + if (bio != NULL) + cbio = bio; + else if ((cbio = HTTP_new_bio(server, port, proxy, proxy_port)) == NULL) + return NULL; + + (void)ERR_set_mark(); /* prepare removing any spurious libssl errors */ + if (rbio == NULL && BIO_connect_retry(cbio, timeout) <= 0) + goto end; + /* now timeout is guaranteed to be >= 0 */ + + /* callback can be used to wrap or prepend TLS session */ + if (bio_update_fn != NULL) { + BIO *orig_bio = cbio; + cbio = (*bio_update_fn)(cbio, arg, 1 /* connect */, use_ssl); + if (cbio == NULL) { + cbio = orig_bio; + goto end; + } + } + + rctx = 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), + expected_ct, expect_asn1); + if (rctx == NULL) + goto end; + + resp = OSSL_HTTP_REQ_CTX_transfer(rctx); + if (resp == NULL) { + if (rctx->redirection_url != NULL) { + if (redirection_url == NULL) + HTTPerr(0, HTTP_R_REDIRECTION_NOT_ENABLED); + else + /* may be NULL if out of memory: */ + *redirection_url = OPENSSL_strdup(rctx->redirection_url); + } else { + char buf[200]; + unsigned long err = ERR_peek_error(); + int lib = ERR_GET_LIB(err); + int reason = ERR_GET_REASON(err); + + if (lib == ERR_LIB_SSL || lib == ERR_LIB_HTTP + || (lib == ERR_LIB_BIO && reason == BIO_R_CONNECT_TIMEOUT) + || (lib == ERR_LIB_BIO && reason == BIO_R_CONNECT_ERROR) + || (lib == ERR_LIB_CMP + && reason == CMP_R_POTENTIALLY_INVALID_CERTIFICATE)) { + BIO_snprintf(buf, 200, "server=%s:%s", server, port); + ERR_add_error_data(1, buf); + if (err == 0) { + BIO_snprintf(buf, 200, "server has disconnected%s", + use_ssl ? " violating the protocol" : + ", likely because it requires the use of TLS"); + ERR_add_error_data(1, buf); + } + } + } + } + OSSL_HTTP_REQ_CTX_free(rctx); + + /* callback can be used to clean up TLS session */ + if (bio_update_fn != NULL + && (*bio_update_fn)(cbio, arg, 0, resp != NULL) == NULL) { + BIO_free(resp); + resp = NULL; + } + + end: + /* + * Use BIO_free_all() because bio_update_fn may prepend or append to cbio. + * This also frees any (e.g., SSL/TLS) BIOs linked with bio and, + * like BIO_reset(bio), calls SSL_shutdown() to notify/alert the peer. + */ + if (bio == NULL) /* cbio was not provided by caller */ + BIO_free_all(cbio); + + if (resp != NULL) + /* remove any spurious error queue entries by ssl_add_cert_chain() */ + (void)ERR_pop_to_mark(); + else + (void)ERR_clear_last_mark(); + + return resp; +} + +static int redirection_ok(int n_redir, const char *old_url, const char *new_url) +{ + static const char https[] = "https:"; + int https_len = 6; /* strlen(https) */ + + if (n_redir >= HTTP_VERSION_MAX_REDIRECTIONS) { + HTTPerr(0, HTTP_R_TOO_MANY_REDIRECTIONS); + return 0; + } + if (*new_url == '/') /* redirection to same server => same protocol */ + return 1; + if (strncmp(old_url, https, https_len) == 0 && + strncmp(new_url, https, https_len) != 0) { + HTTPerr(0, HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP); + return 0; + } + return 1; +} + +/* Get data via HTTP from server at given URL, potentially with redirection */ +BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *proxy_port, + 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_content_type, int expect_asn1) +{ + time_t start_time = timeout > 0 ? time(NULL) : 0; + char *current_url, *redirection_url; + int n_redirs = 0; + char *host; + char *port; + char *path; + int use_ssl; + BIO *resp = NULL; + + if (url == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + if ((current_url = OPENSSL_strdup(url)) == NULL) + return NULL; + + for (;;) { + if (!OSSL_HTTP_parse_url(current_url, &host, &port, &path, &use_ssl)) + break; + + new_rpath: + resp = OSSL_HTTP_transfer(host, port, path, use_ssl, proxy, proxy_port, + bio, rbio, + bio_update_fn, arg, headers, NULL, NULL, + maxline, max_resp_len, + update_timeout(timeout, start_time), + expected_content_type, expect_asn1, + &redirection_url); + OPENSSL_free(path); + if (resp == NULL && redirection_url != NULL) { + if (redirection_ok(++n_redirs, current_url, redirection_url)) { + (void)BIO_reset(bio); + OPENSSL_free(current_url); + current_url = redirection_url; + if (*redirection_url == '/') { /* redirection to same server */ + path = OPENSSL_strdup(redirection_url); + goto new_rpath; + } + OPENSSL_free(host); + OPENSSL_free(port); + continue; + } + OPENSSL_free(redirection_url); + } + OPENSSL_free(host); + OPENSSL_free(port); + break; + } + OPENSSL_free(current_url); + 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 *proxy_port, + 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_content_type, + const ASN1_ITEM *it) +{ + BIO *mem; + ASN1_VALUE *resp = NULL; + + if (url == NULL || it == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + if ((mem = OSSL_HTTP_get(url, proxy, proxy_port, bio, rbio, bio_update_fn, + arg, headers, maxline, max_resp_len, timeout, + expected_content_type, 1 /* expect_asn1 */)) + != NULL) + resp = BIO_mem_d2i(mem, 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 *proxy_port, + BIO *bio, BIO *rbio, + OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, + const STACK_OF(CONF_VALUE) *headers, + const char *content_type, + 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) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + /* remaining parameters are checked indirectly */ + + req_mem = HTTP_asn1_item2bio(req_it, req); + res_mem = OSSL_HTTP_transfer(server, port, path, use_ssl, proxy, proxy_port, + 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); + if (res_mem != NULL) + resp = BIO_mem_d2i(res_mem, rsp_it); + BIO_free(res_mem); + return resp; +} + +/* BASE64 encoder used for encoding basic proxy authentication credentials */ +static char *base64encode(const void *buf, size_t len) +{ + int i; + size_t outl; + char *out; + + /* Calculate size of encoded data */ + outl = (len / 3); + if (len % 3 > 0) + outl++; + outl <<= 2; + out = OPENSSL_malloc(outl + 1); + if (out == NULL) + return 0; + + i = EVP_EncodeBlock((unsigned char *)out, buf, len); + if (!ossl_assert(0 <= i && (size_t)i <= outl)) { + OPENSSL_free(out); + return NULL; + } + return out; +} + +/* + * Promote the given connection BIO using the CONNECT method for a TLS proxy. + * This is typically called by an app, so bio_err and prog are used unless NULL + * to print additional diagnostic information in a user-oriented way. + */ +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) +{ +# undef BUF_SIZE +# define BUF_SIZE (8 * 1024) + char *mbuf = OPENSSL_malloc(BUF_SIZE); + char *mbufp; + int read_len = 0; + int rv; + int ret = 0; + BIO *fbio = BIO_new(BIO_f_buffer()); + time_t max_time = timeout > 0 ? time(NULL) + timeout : 0; + + if (bio == NULL || server == NULL || port == NULL + || (bio_err != NULL && prog == NULL)) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + goto end; + } + + if (mbuf == NULL || fbio == NULL) { + BIO_printf(bio_err /* may be NULL */, "%s: out of memory", prog); + goto end; + } + BIO_push(fbio, bio); + + BIO_printf(fbio, "CONNECT %s:%s "HTTP_PREFIX"1.0\r\n", server, port); + + /* + * Workaround for broken proxies which would otherwise close + * the connection when entering tunnel mode (e.g., Squid 2.6) + */ + BIO_printf(fbio, "Proxy-Connection: Keep-Alive\r\n"); + + /* Support for basic (base64) proxy authentication */ + if (proxyuser != NULL) { + size_t len = strlen(proxyuser) + 1; + char *proxyauth, *proxyauthenc = NULL; + + if (proxypass != NULL) + len += strlen(proxypass); + proxyauth = OPENSSL_malloc(len + 1); + if (proxyauth == NULL) + goto end; + if (BIO_snprintf(proxyauth, len + 1, "%s:%s", proxyuser, + proxypass != NULL ? proxypass : "") != (int)len) + goto proxy_end; + proxyauthenc = base64encode(proxyauth, len); + if (proxyauthenc != NULL) { + BIO_printf(fbio, "Proxy-Authorization: Basic %s\r\n", proxyauthenc); + OPENSSL_clear_free(proxyauthenc, strlen(proxyauthenc)); + } + proxy_end: + OPENSSL_clear_free(proxyauth, len); + if (proxyauthenc == NULL) + goto end; + } + + /* Terminate the HTTP CONNECT request */ + BIO_printf(fbio, "\r\n"); + + for (;;) { + if (BIO_flush(fbio) != 0) + break; + /* potentially needs to be retried if BIO is non-blocking */ + if (!BIO_should_retry(fbio)) + break; + } + + for (;;) { + /* will not actually wait if timeout == 0 */ + rv = BIO_wait(fbio, max_time); + if (rv <= 0) { + BIO_printf(bio_err, "%s: HTTP CONNECT %s\n", prog, + rv == 0 ? "timed out" : "failed waiting for data"); + goto end; + } + + /*- + * The first line is the HTTP response. + * According to RFC 7230, it is formatted exactly like this: + * HTTP/d.d ddd Reason text\r\n + */ + read_len = BIO_gets(fbio, mbuf, BUF_SIZE); + /* the BIO may not block, so we must wait for the 1st line to come in */ + if (read_len < HTTP_LINE1_MINLEN) + continue; + + /* RFC 7231 4.3.6: any 2xx status code is valid */ + if (strncmp(mbuf, HTTP_PREFIX, strlen(HTTP_PREFIX)) != 0) { + HTTPerr(0, HTTP_R_SERVER_RESPONSE_PARSE_ERROR); + BIO_printf(bio_err, "%s: HTTP CONNECT failed, non-HTTP response\n", + prog); + /* Wrong protocol, not even HTTP, so stop reading headers */ + goto end; + } + mbufp = mbuf + strlen(HTTP_PREFIX); + if (strncmp(mbufp, HTTP_VERSION_PATT, strlen(HTTP_VERSION_PATT)) != 0) { + HTTPerr(0, HTTP_R_SERVER_SENT_WRONG_HTTP_VERSION); + BIO_printf(bio_err, + "%s: HTTP CONNECT failed, bad HTTP version %.*s\n", + prog, HTTP_VERSION_STR_LEN, mbufp); + goto end; + } + mbufp += HTTP_VERSION_STR_LEN; + if (strncmp(mbufp, " 2", strlen(" 2")) != 0) { + mbufp += 1; + /* chop any trailing whitespace */ + while (read_len > 0 && ossl_isspace(mbuf[read_len - 1])) + read_len--; + mbuf[read_len] = '\0'; + HTTPerr(0, HTTP_R_CONNECT_FAILURE); + ERR_add_error_data(2, "Reason=", mbufp); + BIO_printf(bio_err, "%s: HTTP CONNECT failed, Reason=%s\n", + prog, mbufp); + goto end; + } + ret = 1; + break; + } + + /* Read past all following headers */ + do { + /* + * TODO: This does not necessarily catch the case when the full + * HTTP response came in in more than a single TCP message. + */ + read_len = BIO_gets(fbio, mbuf, BUF_SIZE); + } while (read_len > 2); + + end: + if (fbio != NULL) { + (void)BIO_flush(fbio); + BIO_pop(fbio); + BIO_free(fbio); + } + OPENSSL_free(mbuf); + return ret; +# undef BUF_SIZE +} + +#endif /* !defined(OPENSSL_NO_SOCK) */ diff --git a/crypto/http/http_err.c b/crypto/http/http_err.c new file mode 100644 index 0000000000..8618539365 --- /dev/null +++ b/crypto/http/http_err.c @@ -0,0 +1,67 @@ +/* + * Generated by util/mkerr.pl DO NOT EDIT + * Copyright 1995-2019 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 + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <openssl/err.h> +#include <openssl/httperr.h> + +#ifndef OPENSSL_NO_ERR + +static const ERR_STRING_DATA HTTP_str_reasons[] = { + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_ASN1_LEN_EXCEEDS_MAX_RESP_LEN), + "asn1 len exceeds max resp len"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_CONNECT_FAILURE), "connect failure"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_ERROR_PARSING_ASN1_LENGTH), + "error parsing asn1 length"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_ERROR_PARSING_CONTENT_LENGTH), + "error parsing content length"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_ERROR_PARSING_URL), "error parsing url"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_ERROR_RECEIVING), "error receiving"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_ERROR_SENDING), "error sending"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_INCONSISTENT_CONTENT_LENGTH), + "inconsistent content length"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_MAX_RESP_LEN_EXCEEDED), + "max resp len exceeded"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_MISSING_ASN1_ENCODING), + "missing asn1 encoding"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_MISSING_CONTENT_TYPE), + "missing content type"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_MISSING_REDIRECT_LOCATION), + "missing redirect location"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP), + "redirection from https to http"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_REDIRECTION_NOT_ENABLED), + "redirection not enabled"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_RESPONSE_LINE_TOO_LONG), + "response line too long"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_SERVER_RESPONSE_PARSE_ERROR), + "server response parse error"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_SERVER_SENT_ERROR), "server sent error"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_SERVER_SENT_WRONG_HTTP_VERSION), + "server sent wrong http version"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_STATUS_CODE_UNSUPPORTED), + "status code unsupported"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_TLS_NOT_ENABLED), "tls not enabled"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_TOO_MANY_REDIRECTIONS), + "too many redirections"}, + {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_UNEXPECTED_CONTENT_TYPE), + "unexpected content type"}, + {0, NULL} +}; + +#endif + +int ERR_load_HTTP_strings(void) +{ +#ifndef OPENSSL_NO_ERR + if (ERR_reason_error_string(HTTP_str_reasons[0].error) == NULL) + ERR_load_strings_const(HTTP_str_reasons); +#endif + return 1; +} diff --git a/crypto/http/http_lib.c b/crypto/http/http_lib.c new file mode 100644 index 0000000000..1d7ad0422a --- /dev/null +++ b/crypto/http/http_lib.c @@ -0,0 +1,116 @@ +/* + * Copyright 2001-2020 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 + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <openssl/http.h> +#include <openssl/httperr.h> +#include <openssl/err.h> +#include <string.h> + +/* + * Parse a URL and split it up into host, port and path components and + * whether it indicates SSL/TLS. Return 1 on success, 0 on error. + */ + +int OSSL_HTTP_parse_url(const char *url, char **phost, char **pport, + char **ppath, int *pssl) +{ + char *p, *buf; + char *host; + char *port = "80"; + + if (url == NULL) { + HTTPerr(0, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (phost != NULL) + *phost = NULL; + if (pport != NULL) + *pport = NULL; + if (ppath != NULL) + *ppath = NULL; + if (pssl != NULL) + *pssl = 0; + + /* dup the buffer since we are going to mess with it */ + if ((buf = OPENSSL_strdup(url)) == NULL) + goto err; + + /* Check for initial colon */ + p = strchr(buf, ':'); + if (p == NULL || p - buf > 5 /* strlen("https") */) { + p = buf; + } else { + *(p++) = '\0'; + + if (strcmp(buf, "https") == 0) { + if (pssl != NULL) + *pssl = 1; + port = "443"; + } else if (strcmp(buf, "http") != 0) { + goto parse_err; + } + + /* Check for double slash */ + if ((p[0] != '/') || (p[1] != '/')) + goto parse_err; + p += 2; + } + host = p; + + /* Check for trailing part of path */ + p = strchr(p, '/'); + if (ppath != NULL && (*ppath = OPENSSL_strdup(p == NULL ? "/" : p)) == NULL) + goto err; + if (p != NULL) + *p = '\0'; /* Set start of path to 0 so hostname[:port] is valid */ + + p = host; + if (host[0] == '[') { + /* ipv6 literal */ + host++; + p = strchr(host, ']'); + if (p == NULL) + goto parse_err; + *p = '\0'; + p++; + } + + /* Look for optional ':' for port number */ + if ((p = strchr(p, ':'))) { + *p = '\0'; + port = p + 1; + } + if (phost != NULL && (*phost = OPENSSL_strdup(host)) == NULL) + goto err; + if (pport != NULL && (*pport = OPENSSL_strdup(port)) == NULL) + goto err; + + OPENSSL_free(buf); + return 1; + + parse_err: + HTTPerr(0, HTTP_R_ERROR_PARSING_URL); + + err: + if (ppath != NULL) { + OPENSSL_free(*ppath); + *ppath = NULL; + } + if (pport != NULL) { + OPENSSL_free(*pport); + *pport = NULL; + } + if (phost != NULL) { + OPENSSL_free(*phost); + *phost = NULL; + } + OPENSSL_free(buf); + return 0; +} diff --git a/crypto/http/http_local.h b/crypto/http/http_local.h new file mode 100644 index 0000000000..33457f1e09 --- /dev/null +++ b/crypto/http/http_local.h @@ -0,0 +1,51 @@ +/* + * Copyright 2007-2020 The OpenSSL Project Authors. All Rights Reserved. + * Copyright Siemens AG 2018-2020 + * + * 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 + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OSSL_CRYPTO_HTTP_LOCAL_H +# define OSSL_CRYPTO_HTTP_LOCAL_H + +# include <openssl/ocsp.h> + +/* name aliases for legacy names with name prefix "OCSP_" */ +typedef OCSP_REQ_CTX OSSL_HTTP_REQ_CTX; +/* functions meanwhile only used internally */ +# define OSSL_HTTP_REQ_CTX_new OCSP_REQ_CTX_new +# define OSSL_HTTP_REQ_CTX_free OCSP_REQ_CTX_free +# define OSSL_HTTP_REQ_CTX_header OCSP_REQ_CTX_http +# define OSSL_HTTP_REQ_CTX_add1_header OCSP_REQ_CTX_add1_header +# define OSSL_HTTP_REQ_CTX_i2d OCSP_REQ_CTX_i2d +# define OSSL_HTTP_REQ_CTX_nbio OCSP_REQ_CTX_nbio +# ifndef OPENSSL_NO_SOCK +# define OSSL_HTTP_REQ_CTX_sendreq_d2i OCSP_REQ_CTX_nbio_d2i +# endif +/* functions that are meanwhile unused */ +# define OSSL_HTTP_REQ_CTX_get0_mem_bio OCSP_REQ_CTX_get0_mem_bio /* undoc'd */ +# define OSSL_HTTP_REQ_CTX_set_max_response_length OCSP_set_max_response_length + +BIO *HTTP_asn1_item2bio(const ASN1_ITEM *it, ASN1_VALUE *val); +OSSL_HTTP_REQ_CTX *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); +ASN1_VALUE *HTTP_sendreq_bio(BIO *bio, OSSL_HTTP_bio_cb_t bio_update_fn, + void *arg, const char *server, const char *port, + const char *path, int use_ssl, int use_proxy, + const STACK_OF(CONF_VALUE) *headers, + const char *content_type, + ASN1_VALUE *req, const ASN1_ITEM *req_it, + int maxline, unsigned long max_resp_len, + int timeout, const ASN1_ITEM *rsp_it); + +#endif /* !defined OSSL_CRYPTO_HTTP_LOCAL_H */ diff --git a/crypto/ocsp/build.info b/crypto/ocsp/build.info index 0902caae3d..79a59004af 100644 --- a/crypto/ocsp/build.info +++ b/crypto/ocsp/build.info @@ -1,4 +1,4 @@ LIBS=../../libcrypto SOURCE[../../libcrypto]=\ - ocsp_asn.c ocsp_ext.c ocsp_ht.c ocsp_lib.c ocsp_cl.c \ + ocsp_asn.c ocsp_ext.c ocsp_http.c ocsp_lib.c ocsp_cl.c \ ocsp_srv.c ocsp_prn.c ocsp_vfy.c ocsp_err.c v3_ocsp.c diff --git a/crypto/ocsp/ocsp_err.c b/crypto/ocsp/ocsp_err.c index 6e2152b3b2..e271780909 100644 --- a/crypto/ocsp/ocsp_err.c +++ b/crypto/ocsp/ocsp_err.c @@ -21,7 +21,6 @@ static const ERR_STRING_DATA OCSP_str_reasons[] = { "error in nextupdate field"}, {ERR_PACK(ERR_LIB_OCSP, 0, OCSP_R_ERROR_IN_THISUPDATE_FIELD), "error in thisupdate field"}, - {ERR_PACK(ERR_LIB_OCSP, 0, OCSP_R_ERROR_PARSING_URL), "error parsing url"}, {ERR_PACK(ERR_LIB_OCSP, 0, OCSP_R_MISSING_OCSPSIGNING_USAGE), "missing ocspsigning usage"}, {ERR_PACK(ERR_LIB_OCSP, 0, OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE), @@ -41,10 +40,6 @@ static const ERR_STRING_DATA OCSP_str_reasons[] = { "response contains no revocation data"}, {ERR_PACK(ERR_LIB_OCSP, 0, OCSP_R_ROOT_CA_NOT_TRUSTED), "root ca not trusted"}, - {ERR_PACK(ERR_LIB_OCSP, 0, OCSP_R_SERVER_RESPONSE_ERROR), - "server response error"}, - {ERR_PACK(ERR_LIB_OCSP, 0, OCSP_R_SERVER_RESPONSE_PARSE_ERROR), - "server response parse error"}, {ERR_PACK(ERR_LIB_OCSP, 0, OCSP_R_SIGNATURE_FAILURE), "signature failure"}, {ERR_PACK(ERR_LIB_OCSP, 0, OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND), "signer certificate not found"}, diff --git a/crypto/ocsp/ocsp_ht.c b/crypto/ocsp/ocsp_ht.c deleted file mode 100644 index fa147f3b16..0000000000 --- a/crypto/ocsp/ocsp_ht.c +++ /dev/null @@ -1,502 +0,0 @@ -/* - * Copyright 2001-2017 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 - * in the file LICENSE in the source distribution or at - * https://www.openssl.org/source/license.html - */ - -#include "e_os.h" -#include <stdio.h> -#include <stdlib.h> -#include "crypto/ctype.h" -#include <string.h> -#include <openssl/asn1.h> -#include <openssl/ocsp.h> -#include <openssl/err.h> -#include <openssl/buffer.h> - -/* Stateful OCSP request code, supporting non-blocking I/O */ - -/* 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 */ - unsigned long max_resp_len; /* Maximum length of response */ -}; - -#define OCSP_MAX_RESP_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 -/* 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 = OPENSSL_zalloc(sizeof(*rctx)); - - if (rctx == NULL) - return NULL; - rctx->state = OHS_ERROR; - rctx->max_resp_len = OCSP_MAX_RESP_LENGTH; - 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 (rctx->iobuf == NULL || rctx->mem == NULL) { - OCSP_REQ_CTX_free(rctx); - return NULL; - } - return rctx; -} - -void OCSP_REQ_CTX_free(OCSP_REQ_CTX *rctx) -{ - if (!rctx) - return; - BIO_free(rctx->mem); - OPENSSL_free(rctx->iobuf); - OPENSSL_free(rctx); -} - -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"; - int reqlen = ASN1_item_i2d(val, NULL, it); - if (BIO_printf(rctx->mem, req_hdr, reqlen) <= 0) - return 0; - if (ASN1_item_i2d_bio(it, rctx->mem, val) <= 0) - return 0; - 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 == NULL) - 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) -{ - if (!name) - return 0; - if (BIO_puts(rctx->mem, name) <= 0) - return 0; - if (value) { - if (BIO_write(rctx->mem, ": ", 2) != 2) - return 0; - if (BIO_puts(rctx->mem, value) <= 0) - return 0; - } - 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, const char *path, OCSP_REQUEST *req, - int maxline) -{ - - OCSP_REQ_CTX *rctx = NULL; - rctx = OCSP_REQ_CTX_new(io, maxline); - if (rctx == NULL) - return NULL; - - if (!OCSP_REQ_CTX_http(rctx, "POST", path)) - goto err; - - if (req && !OCSP_REQ_CTX_set1_req(rctx, req)) - goto err; - - return rctx; - - err: - OCSP_REQ_CTX_free(rctx); - return NULL; -} - -/* - * 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 = line; *p && !ossl_isspace(*p); p++) - continue; - if (*p == '\0') { - 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 && ossl_isspace(*p)) - p++; - - if (*p == '\0') { - 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 && !ossl_isspace(*q); q++) - continue; - - if (*q == '\0') { - 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) - return 0; - - /* Skip over any leading white space in message */ - while (*q && ossl_isspace(*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; ossl_isspace(*r); r--) - *r = 0; - } - if (retcode != 200) { - OCSPerr(OCSP_F_PARSE_HTTP_LINE1, OCSP_R_SERVER_RESPONSE_ERROR); - if (*q == '\0') - ERR_add_error_data(2, "Code=", p); - else - ERR_add_error_data(4, "Code=", p, ",Reason=", q); - return 0; - } - - return 1; - -} - -int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx) -{ - 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_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; - - /* fall thru */ - case OHS_ASN1_WRITE_INIT: - rctx->asn1_len = BIO_get_mem_data(rctx->mem, NULL); - rctx->state = OHS_ASN1_WRITE; - - /* fall thru */ - 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); - - /* fall thru */ - 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 2 bytes which is enough - * for ASN1 SEQUENCE header and either length field or at least the - * length of the length field. - */ - n = BIO_get_mem_data(rctx->mem, &p); - if (n < 2) - 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) { - /* - * If MSB set on initial length octet we can now always read 6 - * octets: make sure we have them. - */ - if (n < 6) - goto next_io; - 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 > rctx->max_resp_len) { - 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, NULL); - if (n < (int)rctx->asn1_len) - goto next_io; - - rctx->state = OHS_DONE; - return 1; - - case OHS_DONE: - return 1; - - } - - 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 */ - -OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req) -{ - OCSP_RESPONSE *resp = NULL; - OCSP_REQ_CTX *ctx; - int rv; - - ctx = OCSP_sendreq_new(b, path, req, -1); - - if (ctx == NULL) - return NULL; - - 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; -} diff --git a/crypto/ocsp/ocsp_http.c b/crypto/ocsp/ocsp_http.c new file mode 100644 index 0000000000..39277c1bba --- /dev/null +++ b/crypto/ocsp/ocsp_http.c @@ -0,0 +1,65 @@ +/* + * Copyright 2001-2019 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 + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <openssl/ocsp.h> +#include <openssl/http.h> +#include "../http/http_local.h" + +#ifndef OPENSSL_NO_OCSP + +int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, const OCSP_REQUEST *req) +{ + return OCSP_REQ_CTX_i2d(rctx, "application/ocsp-request", + ASN1_ITEM_rptr(OCSP_REQUEST), (ASN1_VALUE *)req); +} + +OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, OCSP_REQUEST *req, + int maxline) +{ + BIO *req_mem = HTTP_asn1_item2bio(ASN1_ITEM_rptr(OCSP_REQUEST), + (ASN1_VALUE *)req); + OCSP_REQ_CTX *res = + HTTP_REQ_CTX_new(io, io, 0 /* no HTTP proxy used */, NULL, NULL, path, + NULL /* headers */, "application/ocsp-request", + req_mem /* may be NULL */, + maxline, 0 /* default max_resp_len */, + 0 /* no timeout, blocking indefinite */, NULL, + 1 /* expect_asn1 */); + BIO_free(req_mem); + return res; +} + +# ifndef OPENSSL_NO_SOCK +int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx) +{ + *presp = (OCSP_RESPONSE *) + OCSP_REQ_CTX_nbio_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; + OCSP_REQ_CTX *ctx; + int rv; + + ctx = OCSP_sendreq_new(b, path, req, -1 /* default max resp line length */); + if (ctx == NULL) + return NULL; + + rv = OCSP_sendreq_nbio(&resp, ctx); + + /* this indirectly calls ERR_clear_error(): */ + OCSP_REQ_CTX_free(ctx); + + return rv == 1 ? resp : NULL; +} +# endif /* !defined(OPENSSL_NO_SOCK) */ + +#endif /* !defined(OPENSSL_NO_OCSP) */ diff --git a/crypto/ocsp/ocsp_lib.c b/crypto/ocsp/ocsp_lib.c index a027062ccf..797ac289d4 100644 --- a/crypto/ocsp/ocsp_lib.c +++ b/crypto/ocsp/ocsp_lib.c @@ -109,111 +109,4 @@ int OCSP_id_cmp(const OCSP_CERTID *a, const OCSP_CERTID *b) return ASN1_INTEGER_cmp(&a->serialNumber, &b->serialNumber); } -/* - * Parse a URL and split it up into host, port and path components and - * whether it is SSL. - */ - -int OCSP_parse_url(const char *url, char **phost, char **pport, char **ppath, - int *pssl) -{ - char *p, *buf; - - char *host, *port; - - *phost = NULL; - *pport = NULL; - *ppath = NULL; - - /* dup the buffer since we are going to mess with it */ - buf = OPENSSL_strdup(url); - if (!buf) - goto mem_err; - - /* Check for initial colon */ - p = strchr(buf, ':'); - if (p == NULL) - goto parse_err; - - *(p++) = '\0'; - - if (strcmp(buf, "http") == 0) { - *pssl = 0; - port = "80"; - } else if (strcmp(buf, "https") == 0) { - *pssl = 1; - port = "443"; - } else - goto parse_err; - - /* Check for double slash */ - if ((p[0] != '/') || (p[1] != '/')) - goto parse_err; - - p += 2; - - host = p; - - /* Check for trailing part of path */ - p = strchr(p, '/'); - if (p == NULL) - *ppath = OPENSSL_strdup("/"); - else { - *ppath = OPENSSL_strdup(p); - /* Set start of path to 0 so hostname is valid */ - *p = '\0'; - } - - if (*ppath == NULL) - goto mem_err; - - p = host; - if (host[0] == '[') { - /* ipv6 literal */ - host++; - p = strchr(host, ']'); - if (p == NULL) - goto parse_err; - *p = '\0'; - p++; - } - - /* Look for optional ':' for port number */ - if ((p = strchr(p, ':'))) { - *p = 0; - port = p + 1; - } - - *pport = OPENSSL_strdup(port); - if (*pport == NULL) - goto mem_err; - - *phost = OPENSSL_strdup(host); - - if (*phost == NULL) - goto mem_err; - - OPENSSL_free(buf); - - return 1; - - mem_err: - OCSPerr(OCSP_F_OCSP_PARSE_URL, ERR_R_MALLOC_FAILURE); - goto err; - - parse_err: - OCSPerr(OCSP_F_OCSP_PARSE_URL, OCSP_R_ERROR_PARSING_URL); - - err: - OPENSSL_free(buf); - OPENSSL_free(*ppath); - *ppath = NULL; - OPENSSL_free(*pport); - *pport = NULL; - OPENSSL_free(*phost); - *phost = NULL; - return 0; - -} - IMPLEMENT_ASN1_DUP_FUNCTION(OCSP_CERTID) diff --git a/crypto/x509/x_all.c b/crypto/x509/x_all.c index 89940a0cc9..6a6748bad4 100644 --- a/crypto/x509/x_all.c +++ b/crypto/x509/x_all.c @@ -20,7 +20,7 @@ #include <openssl/evp.h> #include <openssl/x509.h> #include "crypto/x509.h" -#include <openssl/ocsp.h> +#include <openssl/http.h> #include <openssl/rsa.h> #include <openssl/dsa.h> #include <openssl/x509v3.h> @@ -123,11 +123,21 @@ int X509_sign_ctx(X509 *x, EVP_MD_CTX *ctx) &x->sig_alg, &x->signature, &x->cert_info, ctx); } -#ifndef OPENSSL_NO_OCSP -int X509_http_nbio(OCSP_REQ_CTX *rctx, X509 **pcert) +#if !defined(OPENSSL_NO_SOCK) +static ASN1_VALUE *simple_get_asn1(const char *url, BIO *bio, BIO *rbio, + int timeout, const ASN1_ITEM *it) { - return OCSP_REQ_CTX_nbio_d2i(rctx, - (ASN1_VALUE **)pcert, ASN1_ITEM_rptr(X509)); + return OSSL_HTTP_get_asn1(url, NULL, NULL /* no proxy and port */, bio, + rbio, NULL /* no callback for SSL/TLS */, NULL, + NULL /* headers */, 1024 /* maxline */, + 0 /* max_resp_len */, timeout, + NULL /* expected_content_type */, it); +} + +X509 *X509_load_http(const char *url, BIO *bio, BIO *rbio, int timeout) +{ + return (X509 *)simple_get_asn1(url, bio, rbio, timeout, + ASN1_ITEM_rptr(X509)); } #endif @@ -159,12 +169,11 @@ int X509_CRL_sign_ctx(X509_CRL *x, EVP_MD_CTX *ctx) &x->crl, ctx); } -#ifndef OPENSSL_NO_OCSP -int X509_CRL_http_nbio(OCSP_REQ_CTX *rctx, X509_CRL **pcrl) +#if !defined(OPENSSL_NO_SOCK) +X509_CRL *X509_CRL_load_http(const char *url, BIO *bio, BIO *rbio, int timeout) { - return OCSP_REQ_CTX_nbio_d2i(rctx, - (ASN1_VALUE **)pcrl, - ASN1_ITEM_rptr(X509_CRL)); + return (X509_CRL *)simple_get_asn1(url, bio, rbio, timeout, + ASN1_ITEM_rptr(X509_CRL)); } #endif diff --git a/doc/man3/OCSP_sendreq_new.pod b/doc/man3/OCSP_sendreq_new.pod index 4807d39fde..f14f90848d 100644 --- a/doc/man3/OCSP_sendreq_new.pod +++ b/doc/man3/OCSP_sendreq_new.pod @@ -10,22 +10,22 @@ OCSP_REQ_CTX_set1_req, OCSP_sendreq_bio - OCSP responder query functions #include <openssl/ocsp.h> - OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, OCSP_REQUEST *req, - int maxline); + OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, const 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); - void OCSP_set_max_response_length(OCSP_REQ_CTX *rctx, unsigned long len); + void OCSP_set_max_response_length(OCSP_REQ_CTX *rctx, + unsigned long len); int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx, const char *name, const char *value); - int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, OCSP_REQUEST *req); + int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, const OCSP_REQUEST *req); - OCSP_RESPONSE *OCSP_sendreq_bio(BIO *io, const char *path, OCSP_REQUEST *req, - int maxline); + OCSP_RESPONSE *OCSP_sendreq_bio(BIO *io, const char *path, OCSP_REQUEST *req); =head1 DESCRIPTION @@ -35,14 +35,14 @@ response header maximum line length of B<maxline>. If B<maxline> is zero a default value of 4k is used. The OCSP request B<req> may be set to B<NULL> and provided later if required. -OCSP_sendreq_nbio() performs non-blocking I/O on the OCSP request context -B<rctx>. When the operation is complete it returns the response in B<*presp>. +OCSP_sendreq_nbio() performs I/O on the OCSP request context B<rctx>. +When the operation is complete it returns the response in B<*presp>. OCSP_REQ_CTX_free() frees up the OCSP context B<rctx>. -OCSP_set_max_response_length() sets the maximum response length for B<rctx> -to B<len>. If the response exceeds this length an error occurs. If not -set a default value of 100k is used. +OCSP_set_max_response_length() sets the maximum response length +for B<rctx> to B<len>. If the response exceeds this length an error occurs. +If not set a default value of 100k is used. OCSP_REQ_CTX_add1_header() adds header B<name> with value B<value> to the context B<rctx>. It can be called more than once to add multiple headers. @@ -55,23 +55,21 @@ function should be called after any calls to OCSP_REQ_CTX_add1_header(). OCSP_sendreq_bio() performs an OCSP request using the responder B<io>, the URL path B<path>, the OCSP request B<req> and with a response header maximum line -length of B<maxline>. If B<maxline> is zero a default value of 4k is used. +length 4k. It waits indefinitely on a response. =head1 RETURN VALUES -OCSP_sendreq_new() returns a valid B<OCSP_REQ_CTX> structure or B<NULL> if -an error occurred. +OCSP_sendreq_new() returns a valid B<OCSP_REQ_CTX> structure or B<NULL> +if an error occurred. -OCSP_sendreq_nbio() returns B<1> if the operation was completed successfully, -B<-1> if the operation should be retried and B<0> if an error occurred. - -OCSP_REQ_CTX_add1_header() and OCSP_REQ_CTX_set1_req() return B<1> for success -and B<0> for failure. +OCSP_sendreq_nbio(), OCSP_REQ_CTX_add1_header() and OCSP_REQ_CTX_set1_req() +return B<1> for success and B<0> for failure. OCSP_sendreq_bio() returns the B<OCSP_RESPONSE> structure sent by the responder or B<NULL> if an error occurred. -OCSP_REQ_CTX_free() and OCSP_set_max_response_length() do not return values. +OCSP_REQ_CTX_free() and OCSP_set_max_response_length() +do not return values. =head1 NOTES @@ -92,14 +90,9 @@ a Host header for B<ocsp.com> you would call: OCSP_REQ_CTX_add1_header(ctx, "Host", "ocsp.com"); -If OCSP_sendreq_nbio() indicates an operation should be retried the -corresponding BIO can be examined to determine which operation (read or -write) should be retried and appropriate action taken (for example a select() -call on the underlying socket). - -OCSP_sendreq_bio() does not support retries and so cannot handle non-blocking -I/O efficiently. It is retained for compatibility and its use in new -applications is not recommended. +OCSP_sendreq_bio() does not support timeout nor setting extra headers. +It is retained for compatibility. +Better use B<OCSP_sendreq_nbio()> instead. =head1 SEE ALSO diff --git a/doc/man3/OSSL_CMP_CTX_new.pod b/doc/man3/OSSL_CMP_CTX_new.pod index aa42d55352..b10cfc4801 100644 --- a/doc/man3/OSSL_CMP_CTX_new.pod +++ b/doc/man3/OSSL_CMP_CTX_new.pod @@ -16,7 +16,6 @@ OSSL_CMP_CTX_set_serverPort, OSSL_CMP_CTX_set1_proxyName, OSSL_CMP_CTX_set_proxyPort, OSSL_CMP_DEFAULT_PORT, -OSSL_cmp_http_cb_t, OSSL_CMP_CTX_set_http_cb, OSSL_CMP_CTX_set_http_cb_arg, OSSL_CMP_CTX_get_http_cb_arg, @@ -84,14 +83,11 @@ OSSL_CMP_CTX_set1_senderNonce int OSSL_CMP_CTX_set1_proxyName(OSSL_CMP_CTX *ctx, const char *name); int OSSL_CMP_CTX_set_proxyPort(OSSL_CMP_CTX *ctx, int port); #define OSSL_CMP_DEFAULT_PORT 80 - typedef BIO *(*OSSL_cmp_http_cb_t)(OSSL_CMP_CTX *ctx, BIO *hbio, - unsigned long detail); - int OSSL_CMP_CTX_set_http_cb(OSSL_CMP_CTX *ctx, OSSL_cmp_http_cb_t cb); + int OSSL_CMP_CTX_set_http_cb(OSSL_CMP_CTX *ctx, HTTP_bio_cb_t cb); int OSSL_CMP_CTX_set_http_cb_arg(OSSL_CMP_CTX *ctx, void *arg); void *OSSL_CMP_CTX_get_http_cb_arg(const OSSL_CMP_CTX *ctx); - typedef int (*OSSL_cmp_transfer_cb_t)(OSSL_CMP_CTX *ctx, - const OSSL_CMP_MSG *req, - OSSL_CMP_MSG **res); + typedef OSSL_CMP_MSG *(*OSSL_cmp_transfer_cb_t)(OSSL_CMP_CTX *ctx, + const OSSL_CMP_MSG *req); int OSSL_CMP_CTX_set_transfer_cb(OSSL_CMP_CTX *ctx, OSSL_cmp_transfer_cb_t cb); int OSSL_CMP_CTX_set_transfer_cb_arg(OSSL_CMP_CTX *ctx, void *arg); @@ -324,22 +320,22 @@ for connecting to the CA server. OSSL_CMP_CTX_set_proxyPort() sets the port of the HTTP proxy. Port defaults to OSSL_CMP_DEFAULT_PORT = 80 if not set explicitly. -OSSL_CMP_CTX_set_http_cb() sets the optional http connect/disconnect callback +OSSL_CMP_CTX_set_http_cb() sets the optional BIO connect/disconnect callback function, which has the prototype - typedef BIO *(*OSSL_cmp_http_cb_t)(OSSL_CMP_CTX *ctx, BIO *hbio, - unsigned long detail); + typedef BIO *(*HTTP_bio_cb_t) (BIO *bio, void *ctx, int connect, int detail); -It may modify the HTTP BIO given in the B<hbio> argument -used by OSSL_CMP_MSG_http_perform(). -On connect the B<detail> argument is 1. -On disconnect it is 0 if no error occurred or else the last error code. -For instance, on connect a TLS BIO may be prepended to implement HTTPS, -and on disconnect some error diagnostics and/or cleanup may be done. -The callback function should return NULL to indicate failure. -It may make use of a custom defined argument stored in the ctx -by means of OSSL_CMP_CTX_set_http_cb_arg(), -which may be retrieved again through OSSL_CMP_CTX_get_http_cb_arg(). +The callback may modify the BIO B<bio> provided by OSSL_CMP_MSG_http_perform(), +whereby it may make use of a custom defined argument B<ctx> +stored in the OSSL_CMP_CTX by means of OSSL_CMP_CTX_set_http_cb_arg(). +During connection establishment, just after calling BIO_connect_retry(), +the function is invoked with the B<connect> argument being 1 and the B<detail> +argument being 1 if HTTPS is requested, i.e., SSL/TLS should be enabled. On +disconnect B<connect> is 0 and B<detail> is 1 in case 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. +The function should return NULL to indicate failure. +After disconnect the modified BIO will be deallocated using BIO_free_all(). OSSL_CMP_CTX_set_http_cb_arg() sets an argument, respectively a pointer to a structure containing arguments, @@ -354,18 +350,17 @@ OSSL_CMP_CTX_set_http_cb_arg() or NULL if unset. OSSL_CMP_CTX_set_transfer_cb() sets the message transfer callback function, which has the type - typedef int (*OSSL_cmp_transfer_cb_t)(const OSSL_CMP_CTX *ctx, - const OSSL_CMP_MSG *req, - OSSL_CMP_MSG **res); + typedef OSSL_CMP_MSG *(*OSSL_cmp_transfer_cb_t) (OSSL_CMP_CTX *ctx, + const OSSL_CMP_MSG *req); + Returns 1 on success, 0 on error. Default is NULL, which implies the use of L<OSSL_CMP_MSG_http_perform(3)>. -The callback should send the CMP request it obtains via the B<req> parameter -and on success place the response in the B<*res> output parameter. +The callback should send the CMP request message it obtains via the B<req> +parameter and on success return the response. The transfer callback may make use of a custom defined argument stored in the ctx by means of OSSL_CMP_CTX_set_transfer_cb_arg(), which may be retrieved again through OSSL_CMP_CTX_get_transfer_cb_arg(). -On success the cb must return 0, else a CMP error reason code defined in cmp.h. OSSL_CMP_CTX_set_transfer_cb_arg() sets an argument, respectively a pointer to a diff --git a/doc/man3/OSSL_HTTP_transfer.pod b/doc/man3/OSSL_HTTP_transfer.pod new file mode 100644 index 0000000000..68010cb6bd --- /dev/null +++ b/doc/man3/OSSL_HTTP_transfer.pod @@ -0,0 +1,210 @@ +=pod + +=head1 NAME + +OSSL_HTTP_get, +OSSL_HTTP_get_asn1, +OSSL_HTTP_post_asn1, +OSSL_HTTP_transfer, +OSSL_HTTP_bio_cb_t, +OSSL_HTTP_proxy_connect, +OSSL_HTTP_parse_url +- http client functions + +=head1 SYNOPSIS + + #include <openssl/http.h> + + typedef BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, + int connect, int detail); + BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *proxy_port, + 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_content_type, int expect_asn1); + ASN1_VALUE *OSSL_HTTP_get_asn1(const char *url, + const char *proxy, const char *proxy_port, + 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_content_type, + const ASN1_ITEM *it); + ASN1_VALUE *OSSL_HTTP_post_asn1(const char *server, const char *port, + const char *path, int use_ssl, + const char *proxy, const char *proxy_port, + BIO *bio, BIO *rbio, + OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, + const STACK_OF(CONF_VALUE) *headers, + const char *content_type, + 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 *proxy_port, + 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 OSSL_HTTP_parse_url(const char *url, char **phost, char **pport, + char **ppath, int *pssl); + +=head1 DESCRIPTION + +OSSL_HTTP_get() uses HTTP GET to obtain data (of any type) from the given B<url> +and returns it as a memory BIO. + +OSSL_HTTP_get_asn1() uses HTTP GET to obtain an ASN.1-encoded value +(e.g., an X.509 certificate) with the expected structure specified by B<it> +(e.g., I<ASN1_ITEM_rptr(X509)>) from the given B<url> +and returns it on success as a pointer to I<ASN1_VALUE>. + +OSSL_HTTP_post_asn1() uses the HTTP POST method to send a request B<req> +with the ASN.1 structure defined in B<req_it> and the given B<content_type> to +the given B<server> and optional B<port> and B<path>, which defaults to "/". +If B<use_ssl> is nonzero a TLS connection is requested and the B<bio_update_fn> +parameter, described below, must be provided. +The optional list B<headers> may contain additional custom HTTP header lines. +The expected structure of the response is specified by B<rsp_it>. +On success it returns the response as a pointer to B<ASN1_VALUE>. + +OSSL_HTTP_transfer() exchanges an HTTP request and response with +the given B<server> and optional B<port> and B<path>, which defaults to "/". +If B<use_ssl> is nonzero a TLS connection is requested and the B<bio_update_fn> +parameter, described below, must be provided. +If B<req_mem> 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 B<content_type>. +The optional list B<headers> may contain additional custom HTTP header lines. +If B<req_mem> is NULL (i.e., the HTTP method is GET) and B<redirection_url> +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<OPENSSL_free(3)>. + +The above functions have the following parameters in common. + +If the B<proxy> parameter is not NULL the HTTP client functions connect +via the given proxy and the optionally given B<proxy_port>. +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. + +Typically the B<bio> and B<rbio> parameters are NULL and the client creates a +network BIO internally for connecting to the given server and port (optionally +via a proxy and its port), and uses it for exchanging the request and response. +If B<bio> is given and B<rbio> is NULL then the client uses this BIO instead. +If both B<bio> and B<rbio> are given (which may be memory BIOs for instance) +then no explicit connection is attempted, +B<bio> is used for writing the request, and B<rbio> for reading the response. +As soon as the client has flushed B<bio> the server must be ready to provide +a response or indicate a waiting condition via B<rbio>. + +The B<maxline> parameter specifies the response header maximum line length, +where 0 indicates the default value, which currently is 4k. +The B<max_resp_len> parameter specifies the maximum response length, +where 0 indicates the default value, which currently is 100k. + +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 B<expect_asn1> 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 B<expected_content_type> (or B<expected_ct>, respectively) +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 B<timeout> 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. + +The optional parameter B<bio_update_fn> with its optional argument B<arg> may +be used to modify the connection BIO used by the HTTP client (and cannot be +used when both B<bio> and B<rbio> are given). +B<bio_update_fn> is a BIO connect/disconnect callback function with prototype + + BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail) + +The callback may modify the HTTP BIO provided in the B<bio> argument, +whereby it may make use of a custom defined argument B<arg>, +which may for instance refer to an I<SSL_CTX> structure. +During connection establishment, just after calling BIO_connect_retry(), +the function is invoked with the B<connect> argument being 1 and the B<detail> +argument being 1 if HTTPS is requested, i.e., SSL/TLS should be enabled. +On disconnect B<connect> is 0 and B<detail> 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. +The function should return NULL to indicate failure. +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 */ + 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 */ + } + return hbio; + } + +After disconnect the modified BIO will be deallocated using BIO_free_all(). + +OSSL_HTTP_proxy_connect() may be used by an above BIO connect callback function +to set up an SSL/TLS connection via an HTTP proxy. +It promotes the given BIO B<bio> representing a connection +pre-established with a TLS proxy using the HTTP CONNECT method, +optionally using proxy client credentials B<proxyuser> and B<proxypass>, +to connect with TLS protection ultimately to B<server> and B<port>. +The B<timeout> parameter is used as described above. +Since this function is typically called by appplications such as +L<openssl-s_client(1)> it uses the B<bio_err> and B<prog> parameters (unless +NULL) to print additional diagnostic information in a user-oriented way. + +OSSL_HTTP_parse_url() parses its input string B<url> as a URL and splits it up +into host, port and path components and a flag whether it begins with 'https'. +The host component may be a DNS name or an IPv4 or an IPv6 address. +The port component is optional and defaults to "443" for HTTPS, else "80". +The path component is also optional and defaults to "/". +As far as the result pointer arguments are not NULL it assigns via +them copies of the respective string components. +The strings returned this way must be deallocated by the caller using +L<OPENSSL_free(3)> unless they are NULL, which is their default value on error. + +=head1 RETURN VALUES + +OSSL_HTTP_get(), OSSL_HTTP_get_asn1(), OSSL_HTTP_post_asn1(), and +OSSL_HTTP_transfer() return on success the data received via HTTP, else NULL. +Error conditions include connection/transfer timeout, parse errors, etc. + +OSSL_HTTP_proxy_connect() and OSSL_HTTP_parse_url() +return 1 on success, 0 on error. + +=head1 HISTORY + +OSSL_HTTP_get(), OSSL_HTTP_get_asn1(), OSSL_HTTP_post_asn1(), +OSSL_HTTP_proxy_connect(), and OSSL_HTTP_parse_url() were added in OpenSSL 3.0. + +=head1 COPYRIGHT + +Copyright 2019-2020 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 +in the file LICENSE in the source distribution or at +L<https://www.openssl.org/source/license.html>. + +=cut diff --git a/doc/man3/X509_load_http.pod b/doc/man3/X509_load_http.pod new file mode 100644 index 0000000000..d30253d5ee --- /dev/null +++ b/doc/man3/X509_load_http.pod @@ -0,0 +1,63 @@ +=pod + +=head1 NAME + +X509_load_http, +X509_http_nbio, +X509_CRL_load_http, +X509_CRL_http_nbio +- certificate and CRL loading functions + +=head1 SYNOPSIS + + #include <openssl/x509.h> + + X509 *X509_load_http(const char *url, BIO *bio, BIO *rbio, int timeout); + X509_CRL *X509_CRL_load_http(const char *url, BIO *bio, BIO *rbio, int timeout); + + #define X509_http_nbio(url) + #define X509_CRL_http_nbio(url) + +=head1 DESCRIPTION + +X509_load_http() and X509_CRL_load_http() loads a certificate or a CRL, +respectively, in ASN.1 format using HTTP from the given B<url>. + +If B<bio> is given and B<rbio> is NULL then this BIO is used instead of an +interal one for connecting, writing the request, and reading the response. +If both B<bio> and B<rbio> are given (which may be memory BIOs, for instance) +then no explicit connection is attempted, +B<bio> is used for writing the request, and B<rbio> for reading the response. + +If the B<timeout> 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. + +X509_http_nbio() and X509_CRL_http_nbio() are macros for backward compatibility +that have the same effect as the functions above but with infinite timeout +and without the possiblity to specify custom BIOs. + +=head1 RETURN VALUES + +On success the function yield the loaded value, else NULL. +Error conditions include connection/transfer timeout, parse errors, etc. + +=head1 SEE ALSO + +L<OSSL_HTTP_get_asn1(3)> + +=head1 HISTORY + +X509_load_http() and X509_CRL_load_http() were added in OpenSSL 3.0. + +=head1 COPYRIGHT + +Copyright 2019-2020 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 +in the file LICENSE in the source distribution or at +L<https://www.openssl.org/source/license.html>. + +=cut diff --git a/include/openssl/cmp.h b/include/openssl/cmp.h index d778842b3b..78763248e0 100644 --- a/include/openssl/cmp.h +++ b/include/openssl/cmp.h @@ -18,6 +18,7 @@ # include <openssl/crmf.h> # include <openssl/cmperr.h> # include <openssl/cmp_util.h> +# include <openssl/http.h> /* explicit #includes not strictly needed since implied by the above: */ # include <openssl/types.h> @@ -274,14 +275,11 @@ int OSSL_CMP_CTX_set_serverPort(OSSL_CMP_CTX *ctx, int port); int OSSL_CMP_CTX_set1_proxyName(OSSL_CMP_CTX *ctx, const char *name); int OSSL_CMP_CTX_set_proxyPort(OSSL_CMP_CTX *ctx, int port); # define OSSL_CMP_DEFAULT_PORT 80 -typedef BIO *(*OSSL_cmp_http_cb_t) (OSSL_CMP_CTX *ctx, BIO *hbio, - unsigned long detail); -int OSSL_CMP_CTX_set_http_cb(OSSL_CMP_CTX *ctx, OSSL_cmp_http_cb_t cb); +int OSSL_CMP_CTX_set_http_cb(OSSL_CMP_CTX *ctx, OSSL_HTTP_bio_cb_t cb); int OSSL_CMP_CTX_set_http_cb_arg(OSSL_CMP_CTX *ctx, void *arg); void *OSSL_CMP_CTX_get_http_cb_arg(const OSSL_CMP_CTX *ctx); -typedef int (*OSSL_cmp_transfer_cb_t) (OSSL_CMP_CTX *ctx, - const OSSL_CMP_MSG *req, - OSSL_CMP_MSG **res); +typedef OSSL_CMP_MSG *(*OSSL_cmp_transfer_cb_t) (OSSL_CMP_CTX *ctx, + const OSSL_CMP_MSG *req); int OSSL_CMP_CTX_set_transfer_cb(OSSL_CMP_CTX *ctx, OSSL_cmp_transfer_cb_t cb); int OSSL_CMP_CTX_set_transfer_cb_arg(OSSL_CMP_CTX *ctx, void *arg); void *OSSL_CMP_CTX_get_transfer_cb_arg(const OSSL_CMP_CTX *ctx); diff --git a/include/openssl/cmperr.h b/include/openssl/cmperr.h index b21db04ee8..cd962fb5d1 100644 --- a/include/openssl/cmperr.h +++ b/include/openssl/cmperr.h @@ -63,6 +63,7 @@ int ERR_load_CMP_strings(void); # define CMP_R_NO_STDIO 194 # define CMP_R_NULL_ARGUMENT 103 # define CMP_R_PKISTATUSINFO_NOT_FOUND 132 +# define CMP_R_POTENTIALLY_INVALID_CERTIFICATE 139 # define CMP_R_UNEXPECTED_PKIBODY 133 # define CMP_R_UNKNOWN_ALGORITHM_ID 134 # define CMP_R_UNKNOWN_CERT_TYPE 135 diff --git a/include/openssl/err.h b/include/openssl/err.h index 96b60882f0..17a248ca8d 100644 --- a/include/openssl/err.h +++ b/include/openssl/err.h @@ -113,6 +113,7 @@ struct err_state_st { # define ERR_LIB_PROV 57 # define ERR_LIB_CMP 58 # define ERR_LIB_OSSL_SERIALIZER 59 +# define ERR_LIB_HTTP 60 # define ERR_LIB_USER 128 @@ -140,6 +141,7 @@ struct err_state_st { # define EVPerr(f, r) ERR_raise_data(ERR_LIB_EVP, (r), NULL) # define FIPSerr(f, r) ERR_raise_data(ERR_LIB_FIPS, (r), NULL) # define HMACerr(f, r) ERR_raise_data(ERR_LIB_HMAC, (r), NULL) +# define HTTPerr(f, r) ERR_raise_data(ERR_LIB_HTTP, (r), NULL) # define KDFerr(f, r) ERR_raise_data(ERR_LIB_KDF, (r), NULL) # define OBJerr(f, r) ERR_raise_data(ERR_LIB_OBJ, (r), NULL) # define OCSPerr(f, r) ERR_raise_data(ERR_LIB_OCSP, (r), NULL) diff --git a/include/openssl/http.h b/include/openssl/http.h new file mode 100644 index 0000000000..4201d98cd0 --- /dev/null +++ b/include/openssl/http.h @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2020 The OpenSSL Project Authors. All Rights Reserved. + * Copyright Siemens AG 2018-2020 + * + * 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 + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OPENSSL_HTTP_H +# define OPENSSL_HTTP_H +# pragma once + +# include <openssl/opensslconf.h> + +# include <openssl/bio.h> +# include <openssl/asn1.h> +# include <openssl/conf.h> + + +# ifdef __cplusplus +extern "C" { +# endif + +typedef BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail); +# ifndef OPENSSL_NO_SOCK +BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *proxy_port, + 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_content_type, int expect_asn1); +ASN1_VALUE *OSSL_HTTP_get_asn1(const char *url, + const char *proxy, const char *proxy_port, + 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_content_type, + const ASN1_ITEM *it); +ASN1_VALUE *OSSL_HTTP_post_asn1(const char *server, const char *port, + const char *path, int use_ssl, + const char *proxy, const char *proxy_port, + BIO *bio, BIO *rbio, + OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, + const STACK_OF(CONF_VALUE) *headers, + const char *content_type, + 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 *proxy_port, + 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); +# endif +int OSSL_HTTP_parse_url(const char *url, char **phost, char **pport, + char **ppath, int *pssl); + +# ifdef __cplusplus +} +# endif +#endif /* !defined OPENSSL_HTTP_H */ diff --git a/include/openssl/httperr.h b/include/openssl/httperr.h new file mode 100644 index 0000000000..36dd7cb067 --- /dev/null +++ b/include/openssl/httperr.h @@ -0,0 +1,55 @@ +/* + * Generated by util/mkerr.pl DO NOT EDIT + * Copyright 1995-2019 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 + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#ifndef OPENSSL_HTTPERR_H +# define OPENSSL_HTTPERR_H + +# include <openssl/opensslconf.h> +# include <openssl/symhacks.h> + + +# ifdef __cplusplus +extern "C" +# endif +int ERR_load_HTTP_strings(void); + +/* + * HTTP function codes. + */ +# ifndef OPENSSL_NO_DEPRECATED_3_0 +# endif + +/* + * HTTP reason codes. + */ +# define HTTP_R_ASN1_LEN_EXCEEDS_MAX_RESP_LEN 108 +# define HTTP_R_CONNECT_FAILURE 100 +# define HTTP_R_ERROR_PARSING_ASN1_LENGTH 109 +# define HTTP_R_ERROR_PARSING_CONTENT_LENGTH 119 +# define HTTP_R_ERROR_PARSING_URL 101 +# define HTTP_R_ERROR_RECEIVING 103 +# define HTTP_R_ERROR_SENDING 102 +# define HTTP_R_INCONSISTENT_CONTENT_LENGTH 120 +# define HTTP_R_MAX_RESP_LEN_EXCEEDED 117 +# define HTTP_R_MISSING_ASN1_ENCODING 110 +# define HTTP_R_MISSING_CONTENT_TYPE 121 +# define HTTP_R_MISSING_REDIRECT_LOCATION 111 +# define HTTP_R_REDIRECTION_FROM_HTTPS_TO_HTTP 112 +# define HTTP_R_REDIRECTION_NOT_ENABLED 116 +# define HTTP_R_RESPONSE_LINE_TOO_LONG 113 +# define HTTP_R_SERVER_RESPONSE_PARSE_ERROR 104 +# define HTTP_R_SERVER_SENT_ERROR 105 +# define HTTP_R_SERVER_SENT_WRONG_HTTP_VERSION 106 +# define HTTP_R_STATUS_CODE_UNSUPPORTED 114 +# define HTTP_R_TLS_NOT_ENABLED 107 +# define HTTP_R_TOO_MANY_REDIRECTIONS 115 +# define HTTP_R_UNEXPECTED_CONTENT_TYPE 118 + +#endif diff --git a/include/openssl/ocsp.h b/include/openssl/ocsp.h index b9dc9887de..b15d747abc 100644 --- a/include/openssl/ocsp.h +++ b/include/openssl/ocsp.h @@ -1,5 +1,5 @@ /* - * Copyright 2000-2018 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2000-2020 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 @@ -162,20 +162,32 @@ DECLARE_ASN1_DUP_FUNCTION(OCSP_CERTID) OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req); OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, OCSP_REQUEST *req, int maxline); -int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx); +# ifndef OPENSSL_NO_SOCK int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx); -OCSP_REQ_CTX *OCSP_REQ_CTX_new(BIO *io, int maxline); +# endif +/* The following functions are used only internally */ +int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx); +OCSP_REQ_CTX *OCSP_REQ_CTX_new(BIO *wbio, BIO *rbio, + int method_GET, int maxline, + unsigned long max_resp_len, int timeout, + const char *expected_content_type, + int expect_asn1); 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); +int OCSP_REQ_CTX_i2d(OCSP_REQ_CTX *rctx, const char *content_type, + const ASN1_ITEM *it, ASN1_VALUE *req); +# ifndef OPENSSL_NO_SOCK +ASN1_VALUE *OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx, const ASN1_ITEM *it); +# endif BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx); -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_http(OCSP_REQ_CTX *rctx, + const char *server, const char *port, const char *path); int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx, const char *name, const char *value); +/* End of functions used only internally */ + +/* TODO: remove this (documented but) meanwhile obsolete function? */ +int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, const OCSP_REQUEST *req); OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *dgst, const X509 *subject, const X509 *issuer); @@ -237,8 +249,7 @@ int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, int OCSP_request_verify(OCSP_REQUEST *req, STACK_OF(X509) *certs, X509_STORE *store, unsigned long flags); -int OCSP_parse_url(const char *url, char **phost, char **pport, char **ppath, - int *pssl); +# define OCSP_parse_url OSSL_HTTP_parse_url /* for backward compatibility */ int OCSP_id_issuer_cmp(const OCSP_CERTID *a, const OCSP_CERTID *b); int OCSP_id_cmp(const OCSP_CERTID *a, const OCSP_CERTID *b); @@ -359,5 +370,5 @@ int OCSP_basic_verify(OCSP_BASICRESP *bs, STACK_OF(X509) *certs, # ifdef __cplusplus } # endif -# endif +# endif /* !defined OPENSSL_NO_OCSP */ #endif diff --git a/include/openssl/ocsperr.h b/include/openssl/ocsperr.h index 57f0a90c12..7e3fd0f703 100644 --- a/include/openssl/ocsperr.h +++ b/include/openssl/ocsperr.h @@ -45,11 +45,9 @@ int ERR_load_OCSP_strings(void); # define OCSP_F_OCSP_CHECK_ISSUER 0 # define OCSP_F_OCSP_CHECK_VALIDITY 0 # define OCSP_F_OCSP_MATCH_ISSUERID 0 -# define OCSP_F_OCSP_PARSE_URL 0 # define OCSP_F_OCSP_REQUEST_SIGN 0 # define OCSP_F_OCSP_REQUEST_VERIFY 0 # define OCSP_F_OCSP_RESPONSE_GET1_BASIC 0 -# define OCSP_F_PARSE_HTTP_LINE1 0 # endif /* @@ -59,7 +57,6 @@ int ERR_load_OCSP_strings(void); # define OCSP_R_DIGEST_ERR 102 # define OCSP_R_ERROR_IN_NEXTUPDATE_FIELD 122 # define OCSP_R_ERROR_IN_THISUPDATE_FIELD 123 -# define OCSP_R_ERROR_PARSING_URL 121 # define OCSP_R_MISSING_OCSPSIGNING_USAGE 103 # define OCSP_R_NEXTUPDATE_BEFORE_THISUPDATE 124 # define OCSP_R_NOT_BASIC_RESPONSE 104 @@ -71,8 +68,6 @@ int ERR_load_OCSP_strings(void); # define OCSP_R_REQUEST_NOT_SIGNED 128 # define OCSP_R_RESPONSE_CONTAINS_NO_REVOCATION_DATA 111 # define OCSP_R_ROOT_CA_NOT_TRUSTED 112 -# define OCSP_R_SERVER_RESPONSE_ERROR 114 -# define OCSP_R_SERVER_RESPONSE_PARSE_ERROR 115 # define OCSP_R_SIGNATURE_FAILURE 117 # define OCSP_R_SIGNER_CERTIFICATE_NOT_FOUND 118 # define OCSP_R_STATUS_EXPIRED 125 diff --git a/include/openssl/types.h b/include/openssl/types.h index a49763765e..5761afc097 100644 --- a/include/openssl/types.h +++ b/include/openssl/types.h @@ -184,7 +184,7 @@ typedef struct NAME_CONSTRAINTS_st NAME_CONSTRAINTS; typedef struct crypto_ex_data_st CRYPTO_EX_DATA; -typedef struct ocsp_req_ctx_st OCSP_REQ_CTX; +typedef struct ossl_http_req_ctx_st OCSP_REQ_CTX; /* backward compatibility */ typedef struct ocsp_response_st OCSP_RESPONSE; typedef struct ocsp_responder_id_st OCSP_RESPID; diff --git a/include/openssl/x509.h b/include/openssl/x509.h index 5e553ef4d3..2b4d14ea5f 100644 --- a/include/openssl/x509.h +++ b/include/openssl/x509.h @@ -372,16 +372,10 @@ int X509_signature_print(BIO *bp, const X509_ALGOR *alg, int X509_sign(X509 *x, EVP_PKEY *pkey, const EVP_MD *md); int X509_sign_ctx(X509 *x, EVP_MD_CTX *ctx); -# ifndef OPENSSL_NO_OCSP -int X509_http_nbio(OCSP_REQ_CTX *rctx, X509 **pcert); -# endif 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); -# ifndef OPENSSL_NO_OCSP -int X509_CRL_http_nbio(OCSP_REQ_CTX *rctx, X509_CRL **pcrl); -# endif 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, @@ -395,6 +389,13 @@ int X509_REQ_digest(const X509_REQ *data, const EVP_MD *type, int X509_NAME_digest(const X509_NAME *data, const EVP_MD *type, unsigned char *md, unsigned int *len); +# if !defined(OPENSSL_NO_SOCK) +X509 *X509_load_http(const char *url, BIO *bio, BIO *rbio, int timeout); +# define X509_http_nbio(url) X509_load_http(url, NULL, NULL, 0) +X509_CRL *X509_CRL_load_http(const char *url, BIO *bio, BIO *rbio, int timeout); +# define X509_CRL_http_nbio(url) X509_CRL_load_http(url, NULL, NULL, 0) +# endif + # ifndef OPENSSL_NO_STDIO X509 *d2i_X509_fp(FILE *fp, X509 **x509); int i2d_X509_fp(FILE *fp, const X509 *x509); diff --git a/test/build.info b/test/build.info index 5c606b364c..dcdc345b81 100644 --- a/test/build.info +++ b/test/build.info @@ -328,6 +328,14 @@ IF[{- !$disabled{tests} -}] INCLUDE[ocspapitest]=../include ../apps/include DEPEND[ocspapitest]=../libcrypto libtestutil.a + IF[{- !$disabled{sock} -}] + PROGRAMS{noinst}=http_test + ENDIF + + SOURCE[http_test]=http_test.c + INCLUDE[http_test]=../include ../apps/include + DEPEND[http_test]=../libcrypto libtestutil.a + SOURCE[dtlstest]=dtlstest.c ssltestlib.c INCLUDE[dtlstest]=../include ../apps/include DEPEND[dtlstest]=../libcrypto ../libssl libtestutil.a diff --git a/test/cmp_ctx_test.c b/test/cmp_ctx_test.c index 627df72182..c007cfb35e 100644 --- a/test/cmp_ctx_test.c +++ b/test/cmp_ctx_test.c @@ -301,15 +301,15 @@ static int test_cmp_ctx_log_cb(void) return result; } -static BIO *test_http_cb(OSSL_CMP_CTX *ctx, BIO *hbio, unsigned long detail) +static BIO *test_http_cb(BIO *bio, void *arg, int use_ssl, int detail) { return NULL; } -static int test_transfer_cb(OSSL_CMP_CTX *ctx, const OSSL_CMP_MSG *req, - OSSL_CMP_MSG **res) +static OSSL_CMP_MSG *test_transfer_cb(OSSL_CMP_CTX *ctx, + const OSSL_CMP_MSG *req) { - return 0; + return NULL; } static int test_certConf_cb(OSSL_CMP_CTX *ctx, X509 *cert, int fail_info, @@ -537,6 +537,7 @@ static X509_STORE *X509_STORE_new_1(void) STACK_OF(TYPE)*, NULL, IS_0, \ sk_##TYPE##_new_null(), sk_##TYPE##_free) +typedef OSSL_HTTP_bio_cb_t OSSL_cmp_http_cb_t; #define DEFINE_SET_CB_TEST(FIELD) \ static OSSL_cmp_##FIELD##_t OSSL_CMP_CTX_get_##FIELD(const CMP_CTX *ctx) \ { \ diff --git a/test/http_test.c b/test/http_test.c new file mode 100644 index 0000000000..80e26459c0 --- /dev/null +++ b/test/http_test.c @@ -0,0 +1,181 @@ +/* + * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved. + * Copyright Siemens AG 2020 + * + * 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 + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <openssl/http.h> +#include <openssl/pem.h> +#include <openssl/x509v3.h> +#include <string.h> + +#include "testutil.h" + +static const ASN1_ITEM *x509_it = NULL; +static X509 *x509 = NULL; +#define SERVER "mock.server" +#define PORT "81" +#define RPATH "path/any.crt" +static const char *rpath; + +static X509 *load_pem_cert(const char *file) +{ + X509 *cert = NULL; + BIO *bio = NULL; + + if (!TEST_ptr(bio = BIO_new(BIO_s_file()))) + return NULL; + if (TEST_int_gt(BIO_read_filename(bio, file), 0)) + (void)TEST_ptr(cert = PEM_read_bio_X509(bio, NULL, NULL, NULL)); + + BIO_free(bio); + return cert; +} + +/* + * pretty trivial HTTP mock server: + * for POST, copy request headers+body from mem BIO 'in' as response to 'out' + * for GET, first redirect the request then respond with 'rsp' of ASN1 type 'it' + */ +static int mock_http_server(BIO *in, BIO *out, + ASN1_VALUE *rsp, const ASN1_ITEM *it) +{ + const char *req; + long count = BIO_get_mem_data(in, (unsigned char **)&req); + const char *hdr = (char *)req; + int is_get = count >= 4 && strncmp(hdr, "GET ", 4) == 0; + int len; + + /* first line should contain "<GET or POST> <rpath> HTTP/1.x" */ + if (is_get) + hdr += 4; + else if (TEST_true(count >= 5 && strncmp(hdr, "POST ", 5) == 0)) + hdr += 5; + else + return 0; + + while (*rpath == '/') + rpath++; + while (*hdr == '/') + hdr++; + len = strlen(rpath); + if (!TEST_strn_eq(hdr, rpath, len) || !TEST_char_eq(hdr++[len], ' ')) + return 0; + hdr += len; + len = strlen("HTTP/1."); + if (!TEST_strn_eq(hdr, "HTTP/1.", len)) + return 0; + hdr += len; + /* check for HTTP version 1.0 .. 1.1 */ + if (!TEST_char_le('0', *hdr) || !TEST_char_le(*hdr++, '1')) + return 0; + if (!TEST_char_eq(*hdr++, '\r') || !TEST_char_eq(*hdr++, '\n')) + return 0; + count -= (hdr - req); + if (count <= 0 || out == NULL) + return 0; + + if (is_get && strcmp(rpath, RPATH) == 0) { + rpath = "path/new.crt"; + return BIO_printf(out, "HTTP/1.1 301 Moved Permanently\r\n" + "Location: /%s\r\n\r\n", rpath) > 0; /* same server */ + } + if (BIO_printf(out, "HTTP/1.1 200 OK\r\n") <= 0) + return 0; + if (is_get) { /* construct new header and body */ + if ((len = ASN1_item_i2d(rsp, NULL, it)) <= 0) + return 0; + if (BIO_printf(out, "Content-Type: application/x-x509-ca-cert\r\n" + "Content-Length: %d\r\n\r\n", len) <= 0) + return 0; + return ASN1_item_i2d_bio(it, out, rsp); + } else { + return BIO_write(out, hdr, count) == count; /* echo header and body */ + } +} + +static long http_bio_cb_ex(BIO *bio, int oper, const char *argp, size_t len, + int cmd, long argl, int ret, size_t *processed) +{ + + if (oper == (BIO_CB_CTRL | BIO_CB_RETURN) && cmd == BIO_CTRL_FLUSH) + ret = mock_http_server(bio, (BIO *)BIO_get_callback_arg(bio), + (ASN1_VALUE *)x509, x509_it); + return ret; +} + +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()); + STACK_OF(CONF_VALUE) *headers = NULL; + int res = 0; + + if (wbio == NULL || rbio == 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 /* proxy_port */, + 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 /* proxy_port */, + 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) + ); + res = TEST_ptr(rcert) && TEST_int_eq(X509_cmp(x509, rcert), 0); + + err: + X509_free(rcert); + BIO_free(wbio); + BIO_free(rbio); + sk_CONF_VALUE_pop_free(headers, X509V3_conf_free); + return res; +} + +static int test_http_get_x509(void) +{ + return test_http_x509(1); +} + +static int test_http_post_x509(void) +{ + return test_http_x509(0); +} + +void cleanup_tests(void) +{ + X509_free(x509); +} + +int setup_tests(void) +{ + if (!test_skip_common_options()) { + TEST_error("Error parsing test options\n"); + return 0; + } + + x509_it = ASN1_ITEM_rptr(X509); + if (!TEST_ptr((x509 = load_pem_cert(test_get_argument(0))))) + return 1; + + ADD_TEST(test_http_get_x509); + ADD_TEST(test_http_post_x509); + return 1; +} diff --git a/test/recipes/80-test_http.t b/test/recipes/80-test_http.t new file mode 100644 index 0000000000..2297c5a537 --- /dev/null +++ b/test/recipes/80-test_http.t @@ -0,0 +1,21 @@ +#! /usr/bin/env perl +# Copyright 2020 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 +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + + +use OpenSSL::Test qw/:DEFAULT srctop_file/; +use OpenSSL::Test::Utils; + +setup("test_http"); + +plan tests => 1; + +SKIP: { + skip "sockets disabled", 1 if disabled("sock"); + ok(run(test(["http_test", + srctop_file("test", "certs", "ca-cert.pem")]))); +} diff --git a/util/err-to-raise b/util/err-to-raise index a62ee3b790..7ff7a8aa85 100755 --- a/util/err-to-raise +++ b/util/err-to-raise @@ -42,6 +42,7 @@ s/ENGINEerr\(\w+, *(\w+)\)/ERR_raise(ERR_LIB_ENGINE, $1)/; s/ESSerr\(\w+, *(\w+)\)/ERR_raise(ERR_LIB_ESS, $1)/; s/EVPerr\(\w+, *(\w+)\)/ERR_raise(ERR_LIB_EVP, $1)/; s/FIPSerr\(\w+, *(\w+)\)/ERR_raise(ERR_LIB_FIPS, $1)/; +s/HTTPerr\(\w+, *(\w+)\)/ERR_raise(ERR_LIB_HTTP, $1)/; s/KDFerr\(\w+, *(\w+)\)/ERR_raise(ERR_LIB_KDF, $1)/; s/OBJerr\(\w+, *(\w+)\)/ERR_raise(ERR_LIB_OBJ, $1)/; s/OCSPerr\(\w+, *(\w+)\)/ERR_raise(ERR_LIB_OCSP, $1)/; diff --git a/util/libcrypto.num b/util/libcrypto.num index e648370dd8..aa6ce17e7d 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -245,7 +245,7 @@ RSA_get_ex_data 249 3_0_0 EXIST::FUNCTION:RSA EVP_PKEY_meth_get_decrypt 250 3_0_0 EXIST::FUNCTION: DES_cfb_encrypt 251 3_0_0 EXIST::FUNCTION:DEPRECATEDIN_3_0,DES CMS_SignerInfo_set1_signer_cert 252 3_0_0 EXIST::FUNCTION:CMS -X509_CRL_http_nbio 253 3_0_0 EXIST::FUNCTION:OCSP +X509_CRL_load_http 253 3_0_0 EXIST::FUNCTION:SOCK ENGINE_register_all_ciphers 254 3_0_0 EXIST::FUNCTION:ENGINE SXNET_new 255 3_0_0 EXIST::FUNCTION: EVP_camellia_256_ctr 256 3_0_0 EXIST::FUNCTION:CAMELLIA @@ -266,7 +266,7 @@ WHIRLPOOL_Init 271 3_0_0 EXIST::FUNCTION:DEPRECATEDIN_3 EVP_OpenInit 272 3_0_0 EXIST::FUNCTION:RSA OCSP_response_get1_basic 273 3_0_0 EXIST::FUNCTION:OCSP CRYPTO_gcm128_tag 274 3_0_0 EXIST::FUNCTION: -OCSP_parse_url 275 3_0_0 EXIST::FUNCTION:OCSP +OSSL_HTTP_parse_url 275 3_0_0 EXIST::FUNCTION: UI_get0_test_string 276 3_0_0 EXIST::FUNCTION: CRYPTO_secure_free 277 3_0_0 EXIST::FUNCTION: DSA_print_fp 278 3_0_0 EXIST::FUNCTION:DSA,STDIO @@ -615,7 +615,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:DSA,STDIO -OCSP_REQ_CTX_nbio_d2i 634 3_0_0 EXIST::FUNCTION:OCSP +OCSP_REQ_CTX_nbio_d2i 634 3_0_0 EXIST::FUNCTION:OCSP,SOCK d2i_X509_REQ_fp 635 3_0_0 EXIST::FUNCTION:STDIO DH_OpenSSL 636 3_0_0 EXIST::FUNCTION:DH BN_get_rfc3526_prime_8192 637 3_0_0 EXIST::FUNCTION:DH @@ -1243,7 +1243,7 @@ TS_REQ_set_cert_req 1271 3_0_0 EXIST::FUNCTION:TS TXT_DB_get_by_index 1272 3_0_0 EXIST::FUNCTION: X509_check_ca 1273 3_0_0 EXIST::FUNCTION: DH_get_2048_224 1274 3_0_0 EXIST::FUNCTION:DH -X509_http_nbio 1275 3_0_0 EXIST::FUNCTION:OCSP +X509_load_http 1275 3_0_0 EXIST::FUNCTION:SOCK i2d_AUTHORITY_INFO_ACCESS 1276 3_0_0 EXIST::FUNCTION: EVP_get_cipherbyname 1277 3_0_0 EXIST::FUNCTION: CONF_dump_fp 1278 3_0_0 EXIST::FUNCTION:STDIO @@ -3615,7 +3615,7 @@ EVP_CIPHER_CTX_encrypting 3694 3_0_0 EXIST::FUNCTION: EC_KEY_can_sign 3695 3_0_0 EXIST::FUNCTION:EC PEM_write_bio_RSAPublicKey 3696 3_0_0 EXIST::FUNCTION:RSA X509_CRL_set1_lastUpdate 3697 3_0_0 EXIST::FUNCTION: -OCSP_sendreq_nbio 3698 3_0_0 EXIST::FUNCTION:OCSP +OCSP_sendreq_nbio 3698 3_0_0 EXIST::FUNCTION:OCSP,SOCK 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: @@ -4923,3 +4923,9 @@ RAND_DRBG_get_callback_data ? 3_0_0 EXIST::FUNCTION: BIO_wait ? 3_0_0 EXIST::FUNCTION:SOCK BIO_socket_wait ? 3_0_0 EXIST::FUNCTION:SOCK BIO_connect_retry ? 3_0_0 EXIST::FUNCTION:SOCK +ERR_load_HTTP_strings ? 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_get ? 3_0_0 EXIST::FUNCTION:SOCK +OSSL_HTTP_get_asn1 ? 3_0_0 EXIST::FUNCTION:SOCK +OSSL_HTTP_post_asn1 ? 3_0_0 EXIST::FUNCTION:SOCK +OSSL_HTTP_transfer ? 3_0_0 EXIST::FUNCTION:SOCK +OSSL_HTTP_proxy_connect ? 3_0_0 EXIST::FUNCTION:SOCK diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt index 5e8f6dea0d..a1f0c3487e 100644 --- a/util/missingcrypto.txt +++ b/util/missingcrypto.txt @@ -597,6 +597,7 @@ ERR_load_ENGINE_strings(3) ERR_load_ERR_strings(3) ERR_load_ESS_strings(3) ERR_load_EVP_strings(3) +ERR_load_HTTP_strings(3) ERR_load_KDF_strings(3) ERR_load_OBJ_strings(3) ERR_load_OCSP_strings(3) @@ -1314,7 +1315,6 @@ X509_CRL_diff(3) X509_CRL_get_lastUpdate(3) X509_CRL_get_meth_data(3) X509_CRL_get_nextUpdate(3) -X509_CRL_http_nbio(3) X509_CRL_it(3) X509_CRL_print(3) X509_CRL_print_ex(3) @@ -1460,7 +1460,6 @@ X509_get_default_private_dir(3) X509_get_pubkey_parameters(3) X509_get_signature_type(3) X509_gmtime_adj(3) -X509_http_nbio(3) X509_issuer_and_serial_hash(3) X509_issuer_name_hash(3) X509_issuer_name_hash_old(3) diff --git a/util/other.syms b/util/other.syms index bdcc283718..78d436f73a 100644 --- a/util/other.syms +++ b/util/other.syms @@ -37,6 +37,8 @@ GEN_SESSION_CB datatype OPENSSL_Applink external OPENSSL_CTX datatype NAMING_AUTHORITY datatype +OCSP_parse_url define +OSSL_HTTP_bio_cb_t datatype OSSL_PARAM datatype OSSL_PROVIDER datatype OSSL_SERIALIZER datatype @@ -369,7 +371,6 @@ OSSL_CMP_log4 define OSSL_CMP_severity datatype OSSL_CMP_warn define OSSL_cmp_certConf_cb_t datatype -OSSL_cmp_http_cb_t datatype OSSL_cmp_log_cb_t datatype OSSL_cmp_transfer_cb_t datatype OSSL_PARAM_TYPE define @@ -548,6 +549,8 @@ SSLv23_client_method define SSLv23_method define SSLv23_server_method define TLS_DEFAULT_CIPHERSUITES define deprecated 3.0.0 +X509_CRL_http_nbio define +X509_http_nbio define X509_STORE_set_lookup_crls_cb define X509_STORE_set_verify_func define EVP_PKEY_CTX_set1_id define |