Generalize schmeme parsing of OSSL_HTTP_parse_url() to OSSL_parse_url()
authorDr. David von Oheimb <David.von.Oheimb@siemens.com>
Wed, 17 Feb 2021 16:24:19 +0000 (17:24 +0100)
committerDr. David von Oheimb <dev@ddvo.net>
Mon, 1 Mar 2021 09:30:43 +0000 (10:30 +0100)
Reviewed-by: Richard Levitte <levitte@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/14009)

crypto/err/openssl.txt
crypto/http/http_err.c
crypto/http/http_lib.c
doc/man3/OSSL_HTTP_parse_url.pod
include/openssl/http.h
include/openssl/httperr.h
util/libcrypto.num

index 530e3217e417281a708f830062d4600136d527de..e9a179c362bada61562d7a8047500d7d9c987bd4 100644 (file)
@@ -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
index 20235ba0f8484be9074a35b5497db933ada79fdd..2bb6d97290aa8cb945da8350d3cee5102e65174a 100644 (file)
@@ -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),
index 3c894d862938ad464c19aa95a8235a8772e7b4bf..6b8c15471ea25c1c627752edb61b80a7de761993 100644 (file)
@@ -36,21 +36,21 @@ static void free_pstring(char **pstr)
     }
 }
 
-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)
+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 = OSSL_HTTP_PORT, *port_end;
+    const char *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(pscheme);
     init_pstring(puser);
     init_pstring(phost);
     init_pstring(pport);
@@ -63,19 +63,15 @@ int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
         return 0;
     }
 
-    /* check for optional prefix "http[s]://" */
+    /* check for optional prefix "<scheme>://" */
+    scheme = scheme_end = url;
     p = strstr(url, "://");
     if (p == NULL) {
         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;
-        } else if (strncmp(url, OSSL_HTTP_NAME, strlen(OSSL_HTTP_NAME)) != 0) {
-            ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_URL_PREFIX);
-            goto err;
-        }
+    } else {
+        scheme_end = p;
+        if (scheme_end == scheme)
+            goto parse_err;
         p += strlen("://");
     }
 
@@ -110,11 +106,12 @@ int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
     }
 
     /* parse optional port specification starting with ':' */
+    port = "0"; /* default */
     if (*p == ':')
         port = ++p;
     /* 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) {
+    if (!sscanf(port, "%u", &portnum) || portnum > 65535) {
         ERR_raise(ERR_LIB_HTTP, HTTP_R_INVALID_PORT_NUMBER);
         goto err;
     }
@@ -150,7 +147,8 @@ int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
         frag = tmp + 1;
     }
 
-    if (!copy_substring(phost, host, host_end)
+    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)
@@ -174,6 +172,8 @@ int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
     ERR_raise(ERR_LIB_HTTP, HTTP_R_ERROR_PARSING_URL);
 
  err:
+    free_pstring(pscheme);
+    free_pstring(puser);
     free_pstring(phost);
     free_pstring(pport);
     free_pstring(ppath);
@@ -182,6 +182,63 @@ int OSSL_HTTP_parse_url(const char *url, int *pssl, char **puser, char **phost,
     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;
+    }
+    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);
+    }
+    return 1;
+
+ err:
+    free_pstring(puser);
+    free_pstring(phost);
+    free_pstring(ppath);
+    free_pstring(pquery);
+    free_pstring(pfrag);
+    return 0;
+}
+
 int http_use_proxy(const char *no_proxy, const char *server)
 {
     size_t sl;
index 5933e954da6001189df747354b606db103d17a2b..60589b6bf9e18021c54003e981749da1fba9f211 100644 (file)
@@ -2,6 +2,7 @@
 
 =head1 NAME
 
+OSSL_parse_url,
 OSSL_HTTP_parse_url,
 OCSP_parse_url
 - http utility functions
@@ -10,6 +11,9 @@ OCSP_parse_url
 
  #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,
@@ -24,28 +28,34 @@ L<openssl_user_macros(7)>:
 
 =head1 DESCRIPTION
 
-OSSL_HTTP_parse_url() parses its input string I<url> as a URL of the form
-C<[http[s]://][userinfo@]host[:port][/path][?query][#fragment]> and splits it up
-into userinfo, host, port, path, query, and fragment components
-and a flag indicating whether it begins with C<https>.
+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 "443" for HTTPS, else "80".
+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</>.
-If I<pssl> is not NULL, I<*pssl> is assigned 1 in case parsing was successful
-and the schema part is present and is C<https>, else 0.
-Each non-NULL result pointer argument I<puser>, I<phost>, I<pport>, I<ppath>,
-I<pquery>, and I<pfrag>, is assigned the respective url component.
+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 userinfo, query, and fragment components an empty string is given.
+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 fucntion OCSP_parse_url(url, host, port, path, ssl) is
-equivalent to OSSL_HTTP_parse_url(url, ssl, NULL, host, port, NULL, path, NULL, NULL).
+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
 
index 508428a986b3d435de5e0e1833a7553ebd403fab..c39049918fada7905c233218042adb35ce61ea19 100644 (file)
@@ -96,6 +96,9 @@ 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_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);
index 796bc15a492c18bc41715d198099401b2d744975..af5717d3dcae0c04e7b27b8ad247f9982a35fa32 100644 (file)
@@ -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
index aa3071ec306f72aa0826611ec7eed1c9c8bcb376..0c5318db8f178521f86a25c61c01482f783bd057 100644 (file)
@@ -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: