Commit f55922e8b2 for openssl.org
commit f55922e8b25e663009fafb120f93824da0627bbf
Author: Dr. David von Oheimb <dev@ddvo.net>
Date: Thu Jul 31 17:13:58 2025 +0200
http_lib.c: fix parsing 'scheme' part in OSSL_parse_url()
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
MergeDate: Wed Jun 10 06:26:59 2026
(Merged from https://github.com/openssl/openssl/pull/27357)
diff --git a/crypto/http/http_lib.c b/crypto/http/http_lib.c
index 122dcfb107..ab3142a32d 100644
--- a/crypto/http/http_lib.c
+++ b/crypto/http/http_lib.c
@@ -21,6 +21,7 @@
#define NI_MAXHOST 255
#endif
#include "crypto/ctype.h" /* for ossl_isspace() */
+#define OSSL_URL_SCHEME_SUFFIX "://"
static void init_pstring(char **pstr)
{
@@ -79,16 +80,20 @@ int OSSL_parse_url(const char *url, char **pscheme, char **puser, char **phost,
return 0;
}
- /* check for optional prefix "<scheme>://" */
- scheme = scheme_end = url;
- p = strstr(url, "://");
- if (p == NULL) {
- p = url;
- } else {
- scheme_end = p;
- if (scheme_end == scheme)
- goto parse_err;
- p += strlen("://");
+ /* check for optional prefix "<scheme>://" as per RFC 3986 */
+ scheme = scheme_end = p = url;
+ if (ossl_isalpha(*p)) {
+ while (*p != '\0'
+ && (ossl_isalpha(*p)
+ || ossl_isdigit(*p)
+ || strchr("+-.", *p) != NULL))
+ p++;
+ if (HAS_PREFIX(p, OSSL_URL_SCHEME_SUFFIX)) {
+ scheme_end = p;
+ p += sizeof(OSSL_URL_SCHEME_SUFFIX) - 1;
+ } else {
+ p = url;
+ }
}
/* parse optional "userinfo@" */
diff --git a/test/http_test.c b/test/http_test.c
index 2776a6f7a1..6879f8b664 100644
--- a/test/http_test.c
+++ b/test/http_test.c
@@ -287,8 +287,8 @@ err:
return res;
}
-static int test_http_url_ok(const char *url, int exp_ssl, const char *exp_host,
- const char *exp_port, const char *exp_path)
+static int test_http_url_frag_ok(const char *url, int exp_ssl, const char *exp_host,
+ const char *exp_port, const char *exp_path, const char *exp_frag)
{
char *user, *host, *port, *path, *query, *frag;
int exp_num, num, ssl;
@@ -305,8 +305,8 @@ static int test_http_url_ok(const char *url, int exp_ssl, const char *exp_host,
&& 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)
+ res = TEST_str_eq(frag, exp_frag);
if (res && *query != '\0')
res = TEST_str_eq(query, "q");
OPENSSL_free(user);
@@ -318,6 +318,12 @@ static int test_http_url_ok(const char *url, int exp_ssl, const char *exp_host,
return res;
}
+static int test_http_url_ok(const char *url, int exp_ssl, const char *exp_host,
+ const char *exp_port, const char *exp_path)
+{
+ return test_http_url_frag_ok(url, exp_ssl, exp_host, exp_port, exp_path, "");
+}
+
static int test_http_url_path_query_ok(const char *url, const char *exp_path_qu)
{
char *host, *path;
@@ -349,6 +355,11 @@ static int test_http_url_dns(void)
return test_http_url_ok("host:65535/path", 0, "host", "65535", "/path");
}
+static int test_http_url_ip(void)
+{
+ return test_http_url_ok("1.2.3.4:5678//blahblablah", 0, "1.2.3.4", "5678", "//blahblablah");
+}
+
static int test_http_url_timestamp(void)
{
return test_http_url_ok("host/p/2017-01-03T00:00:00", 0, "host", "80",
@@ -368,7 +379,9 @@ static int test_http_url_path_query(void)
static int test_http_url_userinfo_query_fragment(void)
{
- return test_http_url_ok("user:pass@host/p?q#fr", 0, "host", "80", "/p");
+ return test_http_url_frag_ok("user:pass@host/p?q#fr", 0, "host", "80", "/p", "fr")
+ && test_http_url_frag_ok("host.example.org/some/path#://not-a-scheme/not.a.host:404", 0,
+ "host.example.org", "80", "/some/path", "://not-a-scheme/not.a.host:404");
}
static int test_http_url_at_sign_outside_authority(void)
@@ -652,6 +665,7 @@ int setup_tests(void)
return 0;
ADD_TEST(test_http_url_dns);
+ ADD_TEST(test_http_url_ip);
ADD_TEST(test_http_url_timestamp);
ADD_TEST(test_http_url_path_query);
ADD_TEST(test_http_url_userinfo_query_fragment);