summaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
authorDr. David von Oheimb <David.von.Oheimb@siemens.com>2021-01-28 22:10:47 +0100
committerDr. David von Oheimb <dev@ddvo.net>2021-03-01 10:30:43 +0100
commit7932982b88f5095f60397fe727d27ddf7234f4d6 (patch)
tree791fa288ead387d06147ae627169996e093c115d /crypto
parente60e974414a7e637ff2f946dc2aa24c381a32cc2 (diff)
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@openssl.org> (Merged from https://github.com/openssl/openssl/pull/14009)
Diffstat (limited to 'crypto')
-rw-r--r--crypto/http/http_client.c28
-rw-r--r--crypto/http/http_lib.c191
2 files changed, 127 insertions, 92 deletions
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_lib.c b/crypto/http/http_lib.c
index 028ef12383..3c894d8629 100644
--- a/crypto/http/http_lib.c
+++ b/crypto/http/http_lib.c
@@ -15,55 +15,77 @@
#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;
+ }
+}
+
+static int copy_substring(char **dest, const char *start, const char *end)
+{
+ return dest == NULL
+ || (*dest = OPENSSL_strndup(start, end - start)) != NULL;
+}
-int OSSL_HTTP_parse_url(const char *url, char **phost, char **pport,
- int *pport_num, char **ppath, int *pssl)
+static void free_pstring(char **pstr)
{
- 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 (pstr != NULL) {
+ OPENSSL_free(*pstr);
+ *pstr = NULL;
+ }
+}
+
+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)
+{
+ const char *p, *tmp;
+ const char *user, *user_end;
+ const char *host, *host_end;
+ const char *port = OSSL_HTTP_PORT, *port_end;
+ unsigned int portnum;
+ const char *path, *path_end;
+ const char *query, *query_end;
+ const char *frag, *frag_end;
+
if (pssl != NULL)
*pssl = 0;
+ 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, "://");
+ p = strstr(url, "://");
if (p == NULL) {
- p = buf;
- } else {
- *p = '\0'; /* p points to end of scheme name */
- if (strcmp(buf, OSSL_HTTPS_NAME) == 0) {
+ p = url;
+ } else { /* p points to end of scheme name */
+ if (strncmp(url, OSSL_HTTPS_NAME, strlen(OSSL_HTTPS_NAME)) == 0) {
if (pssl != NULL)
*pssl = 1;
port = OSSL_HTTPS_PORT;
- portnum = 443;
- } else if (strcmp(buf, OSSL_HTTP_NAME) != 0) {
+ } else if (strncmp(url, OSSL_HTTP_NAME, strlen(OSSL_HTTP_NAME)) != 0) {
ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_URL_PREFIX);
goto err;
}
- p += 3;
+ 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 +94,91 @@ 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, '/');
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) /* 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 == ':') {
+ 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 < 1 || 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);
+
+ /* parse optional "?query" */
+ tmp = strchr(p, '?');
+ if (tmp != NULL) {
+ p = tmp;
+ if (pquery != NULL) {
+ path_end = p;
+ query = p + 1;
+ }
+ }
- if (phost != NULL && (*phost = OPENSSL_strdup(host)) == NULL)
- goto err;
- if (pport != NULL && (*pport = OPENSSL_strdup(port)) == NULL)
+ /* 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(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;
- }
- if (phost != NULL) {
- OPENSSL_free(*phost);
- *phost = NULL;
- }
- OPENSSL_free(buf);
+ free_pstring(phost);
+ free_pstring(pport);
+ free_pstring(ppath);
+ free_pstring(pquery);
+ free_pstring(pfrag);
return 0;
}