[openssl] master update
dev at ddvo.net
dev at ddvo.net
Mon Mar 1 09:31:05 UTC 2021
The branch master has been updated
via d546e8e267bfddc1ca310dfa8b9a72ab4f9aac7c (commit)
via 7932982b88f5095f60397fe727d27ddf7234f4d6 (commit)
from e60e974414a7e637ff2f946dc2aa24c381a32cc2 (commit)
- Log -----------------------------------------------------------------
commit d546e8e267bfddc1ca310dfa8b9a72ab4f9aac7c
Author: Dr. David von Oheimb <David.von.Oheimb at siemens.com>
Date: Wed Feb 17 17:24:19 2021 +0100
Generalize schmeme parsing of OSSL_HTTP_parse_url() to OSSL_parse_url()
Reviewed-by: Richard Levitte <levitte at openssl.org>
(Merged from https://github.com/openssl/openssl/pull/14009)
commit 7932982b88f5095f60397fe727d27ddf7234f4d6
Author: Dr. David von Oheimb <David.von.Oheimb at siemens.com>
Date: Thu Jan 28 22:10:47 2021 +0100
OSSL_HTTP_parse_url(): Handle any userinfo, query, and fragment components
Now handle [http[s]://][userinfo@]host[:port][/path][?query][#frag]
by optionally providing any userinfo, query, and frag components.
All usages of this function, which are client-only,
silently ignore userinfo and frag components,
while the query component is taken as part of the path.
Update and extend the unit tests and all affected documentation.
Document and deprecat OCSP_parse_url().
Fixes an issue that came up when discussing FR #14001.
Reviewed-by: Richard Levitte <levitte at openssl.org>
(Merged from https://github.com/openssl/openssl/pull/14009)
-----------------------------------------------------------------------
Summary of changes:
CHANGES.md | 4 +
apps/cmp.c | 3 +-
apps/lib/apps.c | 3 +-
apps/ocsp.c | 7 +-
apps/s_server.c | 10 +-
crypto/err/openssl.txt | 2 +-
crypto/http/http_client.c | 28 ++---
crypto/http/http_err.c | 4 +-
crypto/http/http_lib.c | 260 ++++++++++++++++++++++++++-------------
doc/man1/openssl-cmp.pod.in | 16 ++-
doc/man1/openssl-ocsp.pod.in | 2 +
doc/man1/openssl-s_server.pod.in | 2 +
doc/man3/OSSL_HTTP_parse_url.pod | 83 +++++++++++++
doc/man3/OSSL_HTTP_transfer.pod | 57 +++------
include/openssl/http.h | 17 +--
include/openssl/httperr.h | 2 +-
include/openssl/ocsp.h.in | 2 +-
test/http_test.c | 63 ++++++++--
util/libcrypto.num | 1 +
util/missingcrypto.txt | 1 -
20 files changed, 391 insertions(+), 176 deletions(-)
create mode 100644 doc/man3/OSSL_HTTP_parse_url.pod
diff --git a/CHANGES.md b/CHANGES.md
index 335b492e4f..0bc5f81100 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -119,6 +119,10 @@ OpenSSL 3.0
*Rich Salz and Richard Levitte*
+ * Deprecated `OCSP_parse_url()`, which is replaced with `OSSL_HTTP_parse_url`.
+
+ *David von Oheimb*
+
* Validation of SM2 keys has been separated from the validation of regular EC
keys, allowing to improve the SM2 validation process to reject loaded private
keys that are not conforming to the SM2 ISO standard.
diff --git a/apps/cmp.c b/apps/cmp.c
index 5778fd95a7..d04af4177b 100644
--- a/apps/cmp.c
+++ b/apps/cmp.c
@@ -1855,7 +1855,8 @@ static int setup_client_ctx(OSSL_CMP_CTX *ctx, ENGINE *engine)
CMP_err("missing -server option");
goto err;
}
- if (!OSSL_HTTP_parse_url(opt_server, &server, &port, &portnum, &path, &ssl)) {
+ if (!OSSL_HTTP_parse_url(opt_server, &ssl, NULL /* user */, &server, &port,
+ &portnum, &path, NULL /* q */, NULL /* frag */)) {
CMP_err1("cannot parse -server URL: %s", opt_server);
goto err;
}
diff --git a/apps/lib/apps.c b/apps/lib/apps.c
index 634bebde42..2a5ec6bb65 100644
--- a/apps/lib/apps.c
+++ b/apps/lib/apps.c
@@ -2271,7 +2271,8 @@ ASN1_VALUE *app_http_get_asn1(const char *url, const char *proxy,
return NULL;
}
- if (!OSSL_HTTP_parse_url(url, &server, &port, NULL, NULL, &use_ssl))
+ if (!OSSL_HTTP_parse_url(url, &use_ssl, NULL /* userinfo */, &server, &port,
+ NULL /* port_num, */, NULL, NULL, NULL))
return NULL;
if (use_ssl && ssl_ctx == NULL) {
ERR_raise_data(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER,
diff --git a/apps/ocsp.c b/apps/ocsp.c
index 97f9403ff1..e61774a8a3 100644
--- a/apps/ocsp.c
+++ b/apps/ocsp.c
@@ -275,9 +275,10 @@ int ocsp_main(int argc, char **argv)
OPENSSL_free(tport);
OPENSSL_free(tpath);
thost = tport = tpath = NULL;
- if (!OSSL_HTTP_parse_url(opt_arg(),
- &host, &port, NULL, &path, &use_ssl)) {
- BIO_printf(bio_err, "%s Error parsing URL\n", prog);
+ if (!OSSL_HTTP_parse_url(opt_arg(), &use_ssl, NULL /* userinfo */,
+ &host, &port, NULL /* port_num */,
+ &path, NULL /* qry */, NULL /* frag */)) {
+ BIO_printf(bio_err, "%s Error parsing -url argument\n", prog);
goto end;
}
thost = host;
diff --git a/apps/s_server.c b/apps/s_server.c
index 9bd9338a31..bbbe3cf877 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -472,8 +472,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 (!OSSL_HTTP_parse_url(sk_OPENSSL_STRING_value(aia, 0),
- &host, &port, NULL, &path, &use_ssl)) {
+ if (!OSSL_HTTP_parse_url(sk_OPENSSL_STRING_value(aia, 0), &use_ssl,
+ NULL, &host, &port, NULL, &path, NULL, NULL)) {
BIO_puts(bio_err, "cert_status: can't parse AIA URL\n");
goto err;
}
@@ -1337,10 +1337,10 @@ int s_server_main(int argc, char *argv[])
case OPT_STATUS_URL:
#ifndef OPENSSL_NO_OCSP
s_tlsextstatus = 1;
- if (!OSSL_HTTP_parse_url(opt_arg(),
+ if (!OSSL_HTTP_parse_url(opt_arg(), &tlscstatp.use_ssl, NULL,
&tlscstatp.host, &tlscstatp.port, NULL,
- &tlscstatp.path, &tlscstatp.use_ssl)) {
- BIO_printf(bio_err, "Error parsing URL\n");
+ &tlscstatp.path, NULL, NULL)) {
+ BIO_printf(bio_err, "Error parsing -status_url argument\n");
goto end;
}
#endif
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 530e3217e4..e9a179c362 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -757,7 +757,7 @@ HTTP_R_FAILED_READING_DATA:128:failed reading data
HTTP_R_INCONSISTENT_CONTENT_LENGTH:120:inconsistent content length
HTTP_R_INVALID_PORT_NUMBER:123:invalid port number
HTTP_R_INVALID_URL_PATH:125:invalid url path
-HTTP_R_INVALID_URL_PREFIX:124:invalid url prefix
+HTTP_R_INVALID_URL_SCHEME:124:invalid url scheme
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
diff --git a/crypto/http/http_client.c b/crypto/http/http_client.c
index 56fb876ee6..259bad366b 100644
--- a/crypto/http/http_client.c
+++ b/crypto/http/http_client.c
@@ -75,8 +75,7 @@ struct ossl_http_req_ctx_st {
OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio,
int method_POST, int maxline,
unsigned long max_resp_len,
- int timeout,
- const char *expected_content_type,
+ int timeout, const char *expected_ct,
int expect_asn1)
{
OSSL_HTTP_REQ_CTX *rctx;
@@ -98,7 +97,7 @@ OSSL_HTTP_REQ_CTX *OSSL_HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio,
return NULL;
}
rctx->method_POST = method_POST;
- rctx->expected_ct = expected_content_type;
+ rctx->expected_ct = expected_ct;
rctx->expect_asn1 = expect_asn1;
rctx->resp_len = 0;
OSSL_HTTP_REQ_CTX_set_max_response_length(rctx, max_resp_len);
@@ -298,8 +297,7 @@ OSSL_HTTP_REQ_CTX *HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int use_http_proxy,
const char *content_type, BIO *req_mem,
int maxline, unsigned long max_resp_len,
int timeout,
- const char *expected_content_type,
- int expect_asn1)
+ const char *expected_ct, int expect_asn1)
{
OSSL_HTTP_REQ_CTX *rctx;
@@ -311,7 +309,7 @@ OSSL_HTTP_REQ_CTX *HTTP_REQ_CTX_new(BIO *wbio, BIO *rbio, int use_http_proxy,
if ((rctx = OSSL_HTTP_REQ_CTX_new(wbio, rbio, req_mem != NULL, maxline,
max_resp_len, timeout,
- expected_content_type, expect_asn1))
+ expected_ct, expect_asn1))
== NULL)
return NULL;
@@ -986,7 +984,7 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
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)
+ const char *expected_ct, int expect_asn1)
{
time_t start_time = timeout > 0 ? time(NULL) : 0;
char *current_url, *redirection_url;
@@ -1005,8 +1003,8 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
return NULL;
for (;;) {
- if (!OSSL_HTTP_parse_url(current_url, &host, &port, NULL /* port_num */,
- &path, &use_ssl))
+ if (!OSSL_HTTP_parse_url(current_url, &use_ssl, NULL /* user */, &host,
+ &port, NULL /* port_num */, &path, NULL, NULL))
break;
new_rpath:
@@ -1015,7 +1013,7 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
bio_update_fn, arg, headers, NULL, NULL,
maxline, max_resp_len,
update_timeout(timeout, start_time),
- expected_content_type, expect_asn1,
+ expected_ct, expect_asn1,
&redirection_url);
OPENSSL_free(path);
if (resp == NULL && redirection_url != NULL) {
@@ -1048,21 +1046,21 @@ ASN1_VALUE *OSSL_HTTP_get_asn1(const char *url,
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)
+ int timeout, const char *expected_ct,
+ const ASN1_ITEM *rsp_it)
{
BIO *mem;
ASN1_VALUE *resp = NULL;
- if (url == NULL || it == NULL) {
+ if (url == NULL || rsp_it == NULL) {
ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}
if ((mem = OSSL_HTTP_get(url, proxy, no_proxy, bio, rbio, bio_update_fn,
arg, headers, maxline, max_resp_len, timeout,
- expected_content_type, 1 /* expect_asn1 */))
+ expected_ct, 1 /* expect_asn1 */))
!= NULL)
- resp = BIO_mem_d2i(mem, it);
+ resp = BIO_mem_d2i(mem, rsp_it);
BIO_free(mem);
return resp;
}
diff --git a/crypto/http/http_err.c b/crypto/http/http_err.c
index 20235ba0f8..2bb6d97290 100644
--- a/crypto/http/http_err.c
+++ b/crypto/http/http_err.c
@@ -32,8 +32,8 @@ static const ERR_STRING_DATA HTTP_str_reasons[] = {
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_INVALID_PORT_NUMBER),
"invalid port number"},
{ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_INVALID_URL_PATH), "invalid url path"},
- {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_INVALID_URL_PREFIX),
- "invalid url prefix"},
+ {ERR_PACK(ERR_LIB_HTTP, 0, HTTP_R_INVALID_URL_SCHEME),
+ "invalid url scheme"},
{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),
diff --git a/crypto/http/http_lib.c b/crypto/http/http_lib.c
index 028ef12383..6b8c15471e 100644
--- a/crypto/http/http_lib.c
+++ b/crypto/http/http_lib.c
@@ -15,55 +15,73 @@
#include "http_local.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.
- */
+static void init_pstring(char **pstr)
+{
+ if (pstr != NULL) {
+ *pstr = NULL;
+ }
+}
-int OSSL_HTTP_parse_url(const char *url, char **phost, char **pport,
- int *pport_num, char **ppath, int *pssl)
+static int copy_substring(char **dest, const char *start, const char *end)
{
- char *p, *buf;
- char *host, *host_end;
- const char *path, *port = OSSL_HTTP_PORT;
- long portnum = 80;
-
- if (phost != NULL)
- *phost = NULL;
- if (pport != NULL)
- *pport = NULL;
- if (ppath != NULL)
- *ppath = NULL;
- if (pssl != NULL)
- *pssl = 0;
+ return dest == NULL
+ || (*dest = OPENSSL_strndup(start, end - start)) != NULL;
+}
+
+static void free_pstring(char **pstr)
+{
+ if (pstr != NULL) {
+ OPENSSL_free(*pstr);
+ *pstr = NULL;
+ }
+}
+
+int OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost,
+ char **pport, int *pport_num,
+ char **ppath, char **pquery, char **pfrag)
+{
+ const char *p, *tmp;
+ const char *scheme, *scheme_end;
+ const char *user, *user_end;
+ const char *host, *host_end;
+ const char *port, *port_end;
+ unsigned int portnum;
+ const char *path, *path_end;
+ const char *query, *query_end;
+ const char *frag, *frag_end;
+
+ init_pstring(pscheme);
+ init_pstring(puser);
+ init_pstring(phost);
+ init_pstring(pport);
+ init_pstring(ppath);
+ init_pstring(pfrag);
+ init_pstring(pquery);
if (url == NULL) {
ERR_raise(ERR_LIB_HTTP, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
- /* dup the buffer since we are going to mess with it */
- if ((buf = OPENSSL_strdup(url)) == NULL)
- goto err;
-
- /* check for optional prefix "http[s]://" */
- p = strstr(buf, "://");
+ /* check for optional prefix "<scheme>://" */
+ scheme = scheme_end = url;
+ p = strstr(url, "://");
if (p == NULL) {
- p = buf;
+ p = url;
} else {
- *p = '\0'; /* p points to end of scheme name */
- if (strcmp(buf, OSSL_HTTPS_NAME) == 0) {
- if (pssl != NULL)
- *pssl = 1;
- port = OSSL_HTTPS_PORT;
- portnum = 443;
- } else if (strcmp(buf, OSSL_HTTP_NAME) != 0) {
- ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_URL_PREFIX);
- goto err;
- }
- p += 3;
+ scheme_end = p;
+ if (scheme_end == scheme)
+ goto parse_err;
+ p += strlen("://");
}
- host = p;
+
+ /* parse optional "userinfo@" */
+ user = user_end = host = p;
+ host = strchr(p, '@');
+ if (host != NULL)
+ user_end = host++;
+ else
+ host = p;
/* parse host name/address as far as needed here */
if (host[0] == '[') {
@@ -72,76 +90,152 @@ int OSSL_HTTP_parse_url(const char *url, char **phost, char **pport,
host_end = strchr(host, ']');
if (host_end == NULL)
goto parse_err;
- *host_end++ = '\0';
+ p = host_end + 1;
} else {
- host_end = strchr(host, ':'); /* look for start of optional port */
+ /* look for start of optional port, path, query, or fragment */
+ host_end = strchr(host, ':');
if (host_end == NULL)
- host_end = strchr(host, '/'); /* look for start of optional path */
+ host_end = strchr(host, '/');
if (host_end == NULL)
- /* the remaining string is just the hostname */
+ host_end = strchr(host, '?');
+ if (host_end == NULL)
+ host_end = strchr(host, '#');
+ if (host_end == NULL) /* the remaining string is just the hostname */
host_end = host + strlen(host);
+ p = host_end;
}
/* parse optional port specification starting with ':' */
- p = host_end;
- if (*p == ':') {
+ port = "0"; /* default */
+ if (*p == ':')
port = ++p;
- if (pport_num == NULL) {
- p = strchr(port, '/');
- if (p == NULL)
- p = host_end + 1 + strlen(port);
- } else { /* make sure a numerical port value is given */
- portnum = strtol(port, &p, 10);
- if (p == port || (*p != '\0' && *p != '/'))
- goto parse_err;
- if (portnum <= 0 || portnum >= 65536) {
- ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_PORT_NUMBER);
- goto err;
- }
- }
+ /* remaining port spec handling is also done for the default values */
+ /* make sure a decimal port number is given */
+ if (!sscanf(port, "%u", &portnum) || portnum > 65535) {
+ ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_PORT_NUMBER);
+ goto err;
}
- *host_end = '\0';
- *p = '\0'; /* terminate port string */
-
- /* check for optional path at end of url starting with '/' */
- path = url + (p - buf);
- /* cannot use p + 1 because *p is '\0' and path must start with '/' */
- if (*path == '\0') {
- path = "/";
- } else if (*path != '/') {
+ for (port_end = port; '0' <= *port_end && *port_end <= '9'; port_end++)
+ ;
+ if (port == p) /* port was given explicitly */
+ p += port_end - port;
+
+ /* check for optional path starting with '/' or '?'. Else must start '#' */
+ path = p;
+ if (*path != '\0' && *path != '/' && *path != '?' && *path != '#') {
ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_URL_PATH);
goto parse_err;
}
+ path_end = query = query_end = frag = frag_end = path + strlen(path);
- if (phost != NULL && (*phost = OPENSSL_strdup(host)) == NULL)
- goto err;
- if (pport != NULL && (*pport = OPENSSL_strdup(port)) == NULL)
+ /* parse optional "?query" */
+ tmp = strchr(p, '?');
+ if (tmp != NULL) {
+ p = tmp;
+ if (pquery != NULL) {
+ path_end = p;
+ query = p + 1;
+ }
+ }
+
+ /* parse optional "#fragment" */
+ tmp = strchr(p, '#');
+ if (tmp != NULL) {
+ if (query == path_end) /* we did not record a query component */
+ path_end = tmp;
+ query_end = tmp;
+ frag = tmp + 1;
+ }
+
+ if (!copy_substring(pscheme, scheme, scheme_end)
+ || !copy_substring(phost, host, host_end)
+ || !copy_substring(pport, port, port_end)
+ || !copy_substring(puser, user, user_end)
+ || !copy_substring(pquery, query, query_end)
+ || !copy_substring(pfrag, frag, frag_end))
goto err;
if (pport_num != NULL)
*pport_num = (int)portnum;
- if (ppath != NULL && (*ppath = OPENSSL_strdup(path)) == NULL)
- goto err;
+ if (*path == '/') {
+ if (!copy_substring(ppath, path, path_end))
+ goto err;
+ } else if (ppath != NULL) { /* must prepend '/' */
+ size_t buflen = 1 + path_end - path + 1;
- OPENSSL_free(buf);
+ if ((*ppath = OPENSSL_malloc(buflen)) == NULL)
+ goto err;
+ snprintf(*ppath, buflen, "/%s", path);
+ }
return 1;
parse_err:
ERR_raise(ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_URL);
err:
- if (ppath != NULL) {
- OPENSSL_free(*ppath);
- *ppath = NULL;
- }
- if (pport != NULL) {
- OPENSSL_free(*pport);
- *pport = NULL;
+ free_pstring(pscheme);
+ free_pstring(puser);
+ free_pstring(phost);
+ free_pstring(pport);
+ free_pstring(ppath);
+ free_pstring(pquery);
+ free_pstring(pfrag);
+ return 0;
+}
+
+int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
+ char **pport, int *pport_num,
+ char **ppath, char **pquery, char **pfrag)
+{
+ char *scheme, *port;
+ int ssl = 0, portnum;
+
+ init_pstring(pport);
+ if (pssl != NULL)
+ *pssl = 0;
+ if (!OSSL_parse_url(url, &scheme, puser, phost, &port, pport_num,
+ ppath, pquery, pfrag))
+ return 0;
+
+ /* check for optional HTTP scheme "http[s]" */
+ if (strcmp(scheme, OSSL_HTTPS_NAME) == 0) {
+ ssl = 1;
+ if (pssl != NULL)
+ *pssl = ssl;
+ } else if (*scheme != '\0' && strcmp(scheme, OSSL_HTTP_NAME) != 0) {
+ ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_URL_SCHEME);
+ OPENSSL_free(scheme);
+ OPENSSL_free(port);
+ goto err;
}
- if (phost != NULL) {
- OPENSSL_free(*phost);
- *phost = NULL;
+ OPENSSL_free(scheme);
+
+ if (strcmp(port, "0") == 0) {
+ /* set default port */
+ OPENSSL_free(port);
+ port = ssl ? OSSL_HTTPS_PORT : OSSL_HTTP_PORT;
+ if (!ossl_assert(sscanf(port, "%d", &portnum) == 1))
+ goto err;
+ if (pport_num != NULL)
+ *pport_num = portnum;
+ if (pport != NULL) {
+ *pport = OPENSSL_strdup(port);
+ if (*pport == NULL)
+ goto err;
+ }
+ } else {
+ if (pport != NULL)
+ *pport = port;
+ else
+ OPENSSL_free(port);
}
- OPENSSL_free(buf);
+ return 1;
+
+ err:
+ free_pstring(puser);
+ free_pstring(phost);
+ free_pstring(ppath);
+ free_pstring(pquery);
+ free_pstring(pfrag);
return 0;
}
diff --git a/doc/man1/openssl-cmp.pod.in b/doc/man1/openssl-cmp.pod.in
index dcb3ceedac..640505e4fb 100644
--- a/doc/man1/openssl-cmp.pod.in
+++ b/doc/man1/openssl-cmp.pod.in
@@ -47,9 +47,9 @@ Certificate enrollment and revocation options:
Message transfer options:
-[B<-server> I<[http[s]://]address[:port][/path]>]
+[B<-server> I<[http[s]://][userinfo@]host[:port][/path][?query][#fragment]>]
[B<-path> I<remote_path>]
-[B<-proxy> I<[http[s]://]address[:port][/path]>]
+[B<-proxy> I<[http[s]://][userinfo@]host[:port][/path][?query][#fragment]>]
[B<-no_proxy> I<addresses>]
[B<-msg_timeout> I<seconds>]
[B<-total_timeout> I<seconds>]
@@ -429,11 +429,13 @@ Reason numbers defined in RFC 5280 are:
=over 4
-=item B<-server> I<[http[s]://]address[:port][/path]>
+=item B<-server> I<[http[s]://][userinfo@]host[:port][/path][?query][#fragment]>
The IP address or DNS hostname and optionally port (defaulting to 80 or 443)
of the CMP server to connect to using HTTP(S) transport.
-The optional I<http://> or I<https://> prefix is ignored.
+The scheme I<https> may be given only if the B<tls_used> option is used.
+The optional userinfo and fragment components are ignored.
+Any given query component is handled as part of the path component.
If a path is included it provides the default value for the B<-path> option.
=item B<-path> I<remote_path>
@@ -441,11 +443,13 @@ If a path is included it provides the default value for the B<-path> option.
HTTP path at the CMP server (aka CMP alias) to use for POST requests.
Defaults to any path given with B<-server>, else C<"/">.
-=item B<-proxy> I<[http[s]://]address[:port][/path]>
+=item B<-proxy> I<[http[s]://][userinfo@]host[:port] [/path][?query][#fragment]>
The HTTP(S) proxy server to use for reaching the CMP server unless B<no_proxy>
applies, see below.
-The optional I<http://> or I<https://> prefix and any trailing path are ignored.
+The optional I<http://> or I<https://> prefix is ignored (note that TLS may be
+selected by B<tls_used>), as well as any path, userinfo, and query, and fragment
+components.
Defaults to the environment variable C<http_proxy> if set, else C<HTTP_PROXY>
in case no TLS is used, otherwise C<https_proxy> if set, else C<HTTPS_PROXY>.
diff --git a/doc/man1/openssl-ocsp.pod.in b/doc/man1/openssl-ocsp.pod.in
index 8cd9c7de19..96fe6acbc9 100644
--- a/doc/man1/openssl-ocsp.pod.in
+++ b/doc/man1/openssl-ocsp.pod.in
@@ -157,6 +157,8 @@ with B<-serial>, B<-cert> and B<-host> options).
=item B<-url> I<responder_url>
Specify the responder URL. Both HTTP and HTTPS (SSL/TLS) URLs can be specified.
+The optional userinfo and fragment components are ignored.
+Any given query component is handled as part of the path component.
=item B<-host> I<hostname>:I<port>, B<-path> I<pathname>
diff --git a/doc/man1/openssl-s_server.pod.in b/doc/man1/openssl-s_server.pod.in
index cb6a1378a0..2bc307dca1 100644
--- a/doc/man1/openssl-s_server.pod.in
+++ b/doc/man1/openssl-s_server.pod.in
@@ -470,6 +470,8 @@ Sets the timeout for OCSP response to I<int> seconds.
Sets a fallback responder URL to use if no responder URL is present in the
server certificate. Without this option an error is returned if the server
certificate does not contain a responder address.
+The optional userinfo and fragment URL components are ignored.
+Any given query component is handled as part of the path component.
=item B<-status_file> I<infile>
diff --git a/doc/man3/OSSL_HTTP_parse_url.pod b/doc/man3/OSSL_HTTP_parse_url.pod
new file mode 100644
index 0000000000..60589b6bf9
--- /dev/null
+++ b/doc/man3/OSSL_HTTP_parse_url.pod
@@ -0,0 +1,83 @@
+=pod
+
+=head1 NAME
+
+OSSL_parse_url,
+OSSL_HTTP_parse_url,
+OCSP_parse_url
+- http utility functions
+
+=head1 SYNOPSIS
+
+ #include <openssl/http.h>
+
+ int OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost,
+ char **pport, int *pport_num,
+ char **ppath, char **pquery, char **pfrag);
+ int OSSL_HTTP_parse_url(const char *url,
+ int *pssl, char **puser, char **phost,
+ char **pport, int *pport_num,
+ char **ppath, char **pquery, char **pfrag);
+
+Deprecated since OpenSSL 3.0, can be hidden entirely by defining
+B<OPENSSL_API_COMPAT> with a suitable version value, see
+L<openssl_user_macros(7)>:
+
+ int OCSP_parse_url(const char *url, char **phost, char **pport, char **ppath,
+ int *pssl);
+
+=head1 DESCRIPTION
+
+OSSL_parse_url() parses its input string I<url> as a URL of the form
+C<[scheme://][userinfo@]host[:port][/path][?query][#fragment]> and splits it up
+into scheme, userinfo, host, port, path, query, and fragment components.
+The host component may be a DNS name or an IP address
+where IPv6 addresses should be enclosed in square brackets C<[> and C<]>.
+The port component is optional and defaults to C<0>.
+If given, it must be in decimal form. If the I<pport_num> argument is not NULL
+the integer value of the port number is assigned to I<*pport_num> on success.
+The path component is also optional and defaults to C</>.
+Each non-NULL result pointer argument I<pscheme>, I<puser>, I<phost>, I<pport>,
+I<ppath>, I<pquery>, and I<pfrag>, is assigned the respective url component.
+On success, they are guaranteed to contain non-NULL string pointers, else NULL.
+It is the reponsibility of the caller to free them using L<OPENSSL_free(3)>.
+If I<pquery> is NULL, any given query component is handled as part of the path.
+A string returned via I<*ppath> is guaranteed to begin with a C</> character.
+For absent scheme, userinfo, port, query, and fragment components
+an empty string is provided.
+
+OSSL_HTTP_parse_url() is a special form of OSSL_parse_url()
+where the scheme, if given, must be C<http> or C<https>.
+If I<pssl> is not NULL, I<*pssl> is assigned 1 in case parsing was successful
+and the scheme is C<https>, else 0.
+The port component is optional and defaults to C<443> if the scheme is C<https>,
+else C<80>.
+
+Calling the deprecated function OCSP_parse_url(url, host, port, path, ssl)
+is equivalent to
+OSSL_HTTP_parse_url(url, ssl, NULL, host, port, NULL, path, NULL, NULL).
+
+=head1 RETURN VALUES
+
+OSSL_HTTP_parse_url() and OCSP_parse_url()
+return 1 on success, 0 on error.
+
+=head1 SEE ALSO
+
+L<OSSL_HTTP_transfer(3)>
+
+=head1 HISTORY
+
+OOSSL_HTTP_parse_url() was added in OpenSSL 3.0.
+OCSP_parse_url() was deprecated in OpenSSL 3.0.
+
+=head1 COPYRIGHT
+
+Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License"). You may not use
+this file except in compliance with the License. You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/OSSL_HTTP_transfer.pod b/doc/man3/OSSL_HTTP_transfer.pod
index cb38d0124f..7de213670d 100644
--- a/doc/man3/OSSL_HTTP_transfer.pod
+++ b/doc/man3/OSSL_HTTP_transfer.pod
@@ -7,8 +7,7 @@ OSSL_HTTP_get_asn1,
OSSL_HTTP_post_asn1,
OSSL_HTTP_transfer,
OSSL_HTTP_bio_cb_t,
-OSSL_HTTP_proxy_connect,
-OSSL_HTTP_parse_url
+OSSL_HTTP_proxy_connect
- http client functions
=head1 SYNOPSIS
@@ -22,15 +21,15 @@ OSSL_HTTP_parse_url
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);
+ const char *expected_ct, int expect_asn1);
ASN1_VALUE *OSSL_HTTP_get_asn1(const char *url,
const char *proxy, const char *no_proxy,
BIO *bio, BIO *rbio,
OSSL_HTTP_bio_cb_t bio_update_fn, void *arg,
const STACK_OF(CONF_VALUE) *headers,
int maxline, unsigned long max_resp_len,
- int timeout, const char *expected_content_type,
- const ASN1_ITEM *it);
+ int timeout, const char *expected_ct,
+ const ASN1_ITEM *rsp_it);
ASN1_VALUE *OSSL_HTTP_post_asn1(const char *server, const char *port,
const char *path, int use_ssl,
const char *proxy, const char *no_proxy,
@@ -54,27 +53,26 @@ OSSL_HTTP_parse_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,
- int *pport_num, char **ppath, int *pssl);
=head1 DESCRIPTION
OSSL_HTTP_get() uses HTTP GET to obtain data (of any type) from the given I<url>
and returns it as a memory BIO.
+If the schema component of the I<url> is C<https> a TLS connection is requested
+and the I<bio_update_fn> parameter, described below, must be provided.
+Any userinfo and fragment components in the I<url> are ignored.
+Any query component is handled as part of the path component.
-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 I<it>
-(e.g., I<ASN1_ITEM_rptr(X509)>) from the given I<url>
+OSSL_HTTP_get_asn1() is like OSSL_HTTP_get() but in addition
+parses the received contents (e.g., an X.509 certificate)
+as an ASN.1 DER encoded value with the expected structure specified by I<rsp_it>
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 I<req>
-with the ASN.1 structure defined in I<req_it> and the given I<content_type> to
-the given I<server> and optional I<port> and I<path>.
+OSSL_HTTP_post_asn1() is like OSSL_HTTP_get_asn1() but uses the HTTP POST method
+to send a request I<req> with the ASN.1 structure defined in I<req_it> and the
+given I<content_type> to the given I<server> and optional I<port> and I<path>.
If I<use_ssl> is nonzero a TLS connection is requested and the I<bio_update_fn>
parameter, described below, must be provided.
-The optional list I<headers> may contain additional custom HTTP header lines.
-The expected structure of the response is specified by I<rsp_it>.
-On success it returns the response as a pointer to B<ASN1_VALUE>.
OSSL_HTTP_transfer() exchanges any form of HTTP request and response.
It implements the core of the functions described above.
@@ -137,7 +135,7 @@ an ASN.1-encoded response is expected, which should include a total length,
the length indications received are checked for consistency
and for not exceeding the maximum response length.
-If the parameter I<expected_content_type> (or I<expected_ct>, respectively)
+If the parameter I<expected_ct>
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.
@@ -192,24 +190,6 @@ Since this function is typically called by applications such as
L<openssl-s_client(1)> it uses the I<bio_err> and I<prog> parameters (unless
NULL) to print additional diagnostic information in a user-oriented way.
-OSSL_HTTP_parse_url() parses its input string I<url> as a URL
-of the form C<[http[s]://]address[:port][/path]> and splits it up into host,
-port, and path components and a flag indicating whether it begins with 'https'.
-The host component may be a DNS name or an IP address
-where IPv6 addresses should be enclosed in square brackets C<[> and C<]>.
-The port component is optional and defaults to "443" for HTTPS, else "80".
-If the I<pport_num> argument is NULL the port specification
-can be in mnemonic form such as "http" like with L<BIO_set_conn_port(3)>, else
-it must be in numerical form and its integer value is assigned to I<*pport_num>.
-The path component is also optional and defaults to "/".
-On success the function assigns via each non-NULL result pointer argument
-I<phost>, I<pport>, I<pport_num>, I<ppath>, and I<pssl>
-the respective url component.
-On error, I<*phost>, I<*pport>, and I<*ppath> are assigned to NULL,
-else they are guaranteed to contain non-NULL string pointers.
-It is the reponsibility of the caller to free them using L<OPENSSL_free(3)>.
-A string returned via I<*ppath> is guaranteed to begin with a C</> character.
-
=head1 NOTES
The names of the environment variables used by this implementation:
@@ -224,17 +204,18 @@ OSSL_HTTP_transfer() return a memory BIO containing the data received via HTTP.
This must be freed by the caller. On failure, NULL is returned.
Failure conditions include connection/transfer timeout, parse errors, etc.
-OSSL_HTTP_proxy_connect() and OSSL_HTTP_parse_url()
-return 1 on success, 0 on error.
+OSSL_HTTP_proxy_connect() returns 1 on success, 0 on error.
=head1 SEE ALSO
+L<OSSL_HTTP_parse_url(3)>
L<BIO_set_conn_port(3)>
=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.
+OSSL_HTTP_transfer(), and OSSL_HTTP_proxy_connect()
+were added in OpenSSL 3.0.
=head1 COPYRIGHT
diff --git a/include/openssl/http.h b/include/openssl/http.h
index 6c3ddd8ce8..c39049918f 100644
--- a/include/openssl/http.h
+++ b/include/openssl/http.h
@@ -41,8 +41,7 @@ typedef BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail)
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 timeout, const char *expected_ct,
int expect_asn1);
void OSSL_HTTP_REQ_CTX_free(OSSL_HTTP_REQ_CTX *rctx);
int OSSL_HTTP_REQ_CTX_set_request_line(OSSL_HTTP_REQ_CTX *rctx,
@@ -64,15 +63,15 @@ BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *no_proxy,
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);
+ const char *expected_ct, int expect_asn1);
ASN1_VALUE *OSSL_HTTP_get_asn1(const char *url,
const char *proxy, const char *no_proxy,
BIO *bio, BIO *rbio,
OSSL_HTTP_bio_cb_t bio_update_fn, void *arg,
const STACK_OF(CONF_VALUE) *headers,
int maxline, unsigned long max_resp_len,
- int timeout, const char *expected_content_type,
- const ASN1_ITEM *it);
+ int timeout, const char *expected_ct,
+ const ASN1_ITEM *rsp_it);
ASN1_VALUE *OSSL_HTTP_post_asn1(const char *server, const char *port,
const char *path, int use_ssl,
const char *proxy, const char *no_proxy,
@@ -97,8 +96,12 @@ 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,
- int *pport_num, char **ppath, int *pssl);
+int OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost,
+ char **pport, int *pport_num,
+ char **ppath, char **pquery, char **pfrag);
+int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
+ char **pport, int *pport_num,
+ char **ppath, char **pquery, char **pfrag);
# ifdef __cplusplus
}
diff --git a/include/openssl/httperr.h b/include/openssl/httperr.h
index 796bc15a49..af5717d3dc 100644
--- a/include/openssl/httperr.h
+++ b/include/openssl/httperr.h
@@ -32,7 +32,7 @@
# define HTTP_R_INCONSISTENT_CONTENT_LENGTH 120
# define HTTP_R_INVALID_PORT_NUMBER 123
# define HTTP_R_INVALID_URL_PATH 125
-# define HTTP_R_INVALID_URL_PREFIX 124
+# define HTTP_R_INVALID_URL_SCHEME 124
# define HTTP_R_MAX_RESP_LEN_EXCEEDED 117
# define HTTP_R_MISSING_ASN1_ENCODING 110
# define HTTP_R_MISSING_CONTENT_TYPE 121
diff --git a/include/openssl/ocsp.h.in b/include/openssl/ocsp.h.in
index 3c5de15494..5e11987dc5 100644
--- a/include/openssl/ocsp.h.in
+++ b/include/openssl/ocsp.h.in
@@ -260,7 +260,7 @@ int OCSP_request_verify(OCSP_REQUEST *req, STACK_OF(X509) *certs,
X509_STORE *store, unsigned long flags);
# define OCSP_parse_url(url, host, port, path, ssl) \
- OSSL_HTTP_parse_url(url, host, port, NULL, path, ssl) /* backward compat */
+ OSSL_HTTP_parse_url(url, ssl, NULL, host, port, NULL, path, NULL, NULL)
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);
diff --git a/test/http_test.c b/test/http_test.c
index 019a6c0f7a..ef0a1d4bf0 100644
--- a/test/http_test.c
+++ b/test/http_test.c
@@ -135,37 +135,75 @@ static int test_http_x509(int do_get)
return res;
}
-static int test_http_url_ok(const char *url, const char *exp_host, int exp_ssl)
+static int test_http_url_ok(const char *url, int exp_ssl, const char *exp_host,
+ const char *exp_port, const char *exp_path)
{
- char *host, *port, *path;
- int num, ssl;
+ char *user, *host, *port, *path, *query, *frag;
+ int exp_num, num, ssl;
int res;
- res = TEST_true(OSSL_HTTP_parse_url(url, &host, &port, &num, &path, &ssl))
+ TEST_int_eq(sscanf(exp_port, "%d", &exp_num), 1);
+ res = TEST_true(OSSL_HTTP_parse_url(url, &ssl, &user, &host, &port, &num,
+ &path, &query, &frag))
&& TEST_str_eq(host, exp_host)
- && TEST_str_eq(port, "65535")
- && TEST_int_eq(num, 65535)
- && TEST_str_eq(path, "/pkix")
+ && TEST_str_eq(port, exp_port)
+ && TEST_int_eq(num, exp_num)
+ && TEST_str_eq(path, exp_path)
&& TEST_int_eq(ssl, exp_ssl);
+ if (res && *user != '\0')
+ res = TEST_str_eq(user, "user:pass");
+ if (res && *frag != '\0')
+ res = TEST_str_eq(frag, "fr");
+ if (res && *query != '\0')
+ res = TEST_str_eq(query, "q");
+ OPENSSL_free(user);
OPENSSL_free(host);
OPENSSL_free(port);
OPENSSL_free(path);
+ OPENSSL_free(query);
+ OPENSSL_free(frag);
+ return res;
+}
+
+static int test_http_url_path_query_ok(const char *url, const char *exp_path_qu)
+{
+ char *host, *path;
+ int res;
+
+ res = TEST_true(OSSL_HTTP_parse_url(url, NULL, NULL, &host, NULL, NULL,
+ &path, NULL, NULL))
+ && TEST_str_eq(host, "host")
+ && TEST_str_eq(path, exp_path_qu);
+ OPENSSL_free(host);
+ OPENSSL_free(path);
return res;
}
static int test_http_url_dns(void)
{
- return test_http_url_ok("server:65535/pkix", "server", 0);
+ return test_http_url_ok("host:65535/path", 0, "host", "65535", "/path");
+}
+
+static int test_http_url_path_query(void)
+{
+ return test_http_url_path_query_ok("http://usr@host:1/p?q=x#frag", "/p?q=x")
+ && test_http_url_path_query_ok("http://host?query#frag", "/?query")
+ && test_http_url_path_query_ok("http://host:9999#frag", "/");
+}
+
+static int test_http_url_userinfo_query_fragment(void)
+{
+ return test_http_url_ok("user:pass at host/p?q#fr", 0, "host", "80", "/p");
}
static int test_http_url_ipv4(void)
{
- return test_http_url_ok("https://1.2.3.4:65535/pkix", "1.2.3.4", 1);
+ return test_http_url_ok("https://1.2.3.4/p/q", 1, "1.2.3.4", "443", "/p/q");
}
static int test_http_url_ipv6(void)
{
- return test_http_url_ok("http://[FF01::101]:65535/pkix", "FF01::101", 0);
+ return test_http_url_ok("http://[FF01::101]:6", 0, "FF01::101", "6", "/");
}
static int test_http_url_invalid(const char *url)
@@ -174,7 +212,8 @@ static int test_http_url_invalid(const char *url)
int num = 1, ssl = 1;
int res;
- res = TEST_false(OSSL_HTTP_parse_url(url, &host, &port, &num, &path, &ssl))
+ res = TEST_false(OSSL_HTTP_parse_url(url, &ssl, NULL, &host, &port, &num,
+ &path, NULL, NULL))
&& TEST_ptr_null(host)
&& TEST_ptr_null(port)
&& TEST_ptr_null(path);
@@ -228,6 +267,8 @@ int setup_tests(void)
return 1;
ADD_TEST(test_http_url_dns);
+ ADD_TEST(test_http_url_path_query);
+ ADD_TEST(test_http_url_userinfo_query_fragment);
ADD_TEST(test_http_url_ipv4);
ADD_TEST(test_http_url_ipv6);
ADD_TEST(test_http_url_invalid_prefix);
diff --git a/util/libcrypto.num b/util/libcrypto.num
index aa3071ec30..0c5318db8f 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -4880,6 +4880,7 @@ ASN1_item_verify_ex ? 3_0_0 EXIST::FUNCTION:
BIO_socket_wait ? 3_0_0 EXIST::FUNCTION:SOCK
BIO_wait ? 3_0_0 EXIST::FUNCTION:
BIO_do_connect_retry ? 3_0_0 EXIST::FUNCTION:
+OSSL_parse_url ? 3_0_0 EXIST::FUNCTION:
OSSL_HTTP_get ? 3_0_0 EXIST::FUNCTION:
OSSL_HTTP_get_asn1 ? 3_0_0 EXIST::FUNCTION:
OSSL_HTTP_post_asn1 ? 3_0_0 EXIST::FUNCTION:
diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt
index 61d91b0c92..60d2572bb2 100644
--- a/util/missingcrypto.txt
+++ b/util/missingcrypto.txt
@@ -829,7 +829,6 @@ OCSP_crlID2_new(3)
OCSP_crlID_new(3)
OCSP_crl_reason_str(3)
OCSP_onereq_get0_id(3)
-OCSP_parse_url(3)
OCSP_request_is_signed(3)
OCSP_request_set1_name(3)
OCSP_request_verify(3)
More information about the openssl-commits
mailing list