]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
urlapi: detect and error on illegal IPv4 addresses
authorDaniel Stenberg <daniel@haxx.se>
Wed, 5 Apr 2023 21:13:33 +0000 (23:13 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 6 Apr 2023 07:02:00 +0000 (09:02 +0200)
Using bad numbers in an IPv4 numerical address now returns
CURLUE_BAD_HOSTNAME.

I noticed while working on trurl and it was originally reported here:
https://github.com/curl/trurl/issues/78

Updated test 1560 accordingly.

Closes #10894

lib/urlapi.c
tests/libtest/lib1560.c

index 7301463b100a3a94bdc7883cfebcac0836440c0d..ece4c4868428f63efa5a83faf80fbc294aae0bc5 100644 (file)
@@ -649,7 +649,12 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname,
  * Output the "normalized" version of that input string in plain quad decimal
  * integers and return TRUE.
  */
-static bool ipv4_normalize(const char *hostname, char *outp, size_t olen)
+
+#define IPV4_NOTANIP 1
+#define IPV4_BAD     2
+#define IPV4_CLEANED 3
+
+static int ipv4_normalize(const char *hostname, char *outp, size_t olen)
 {
   bool done = FALSE;
   int n = 0;
@@ -659,28 +664,18 @@ static bool ipv4_normalize(const char *hostname, char *outp, size_t olen)
   while(!done) {
     char *endp;
     unsigned long l;
-    if((*c < '0') || (*c > '9'))
+    if(!ISDIGIT(*c))
       /* most importantly this doesn't allow a leading plus or minus */
-      return FALSE;
+      return n ? IPV4_BAD :IPV4_NOTANIP;
     l = strtoul(c, &endp, 0);
 
-    /* overflow or nothing parsed at all */
-    if(((l == ULONG_MAX) && (errno == ERANGE)) ||  (endp == c))
-      return FALSE;
-
-#if SIZEOF_LONG > 4
-    /* a value larger than 32 bits */
-    if(l > UINT_MAX)
-      return FALSE;
-#endif
-
     parts[n] = l;
     c = endp;
 
     switch (*c) {
     case '.' :
       if(n == 3)
-        return FALSE;
+        return IPV4_BAD;
       n++;
       c++;
       break;
@@ -690,8 +685,18 @@ static bool ipv4_normalize(const char *hostname, char *outp, size_t olen)
       break;
 
     default:
-      return FALSE;
+      return n ? IPV4_BAD : IPV4_NOTANIP;
     }
+
+    /* overflow */
+    if((l == ULONG_MAX) && (errno == ERANGE))
+      return IPV4_BAD;
+
+#if SIZEOF_LONG > 4
+    /* a value larger than 32 bits */
+    if(l > UINT_MAX)
+      return IPV4_BAD;
+#endif
   }
 
   /* this is deemed a valid IPv4 numerical address */
@@ -704,14 +709,14 @@ static bool ipv4_normalize(const char *hostname, char *outp, size_t olen)
     break;
   case 1: /* a.b -- 8.24 bits */
     if((parts[0] > 0xff) || (parts[1] > 0xffffff))
-      return FALSE;
+      return IPV4_BAD;
     msnprintf(outp, olen, "%u.%u.%u.%u",
               parts[0], (parts[1] >> 16) & 0xff,
               (parts[1] >> 8) & 0xff, parts[1] & 0xff);
     break;
   case 2: /* a.b.c -- 8.8.16 bits */
     if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xffff))
-      return FALSE;
+      return IPV4_BAD;
     msnprintf(outp, olen, "%u.%u.%u.%u",
               parts[0], parts[1], (parts[2] >> 8) & 0xff,
               parts[2] & 0xff);
@@ -719,12 +724,12 @@ static bool ipv4_normalize(const char *hostname, char *outp, size_t olen)
   case 3: /* a.b.c.d -- 8.8.8.8 bits */
     if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff) ||
        (parts[3] > 0xff))
-      return FALSE;
+      return IPV4_BAD;
     msnprintf(outp, olen, "%u.%u.%u.%u",
               parts[0], parts[1], parts[2], parts[3]);
     break;
   }
-  return TRUE;
+  return IPV4_CLEANED;
 }
 
 /* if necessary, replace the host content with a URL decoded version */
@@ -1247,6 +1252,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
 
   if(Curl_dyn_len(&host)) {
     char normalized_ipv4[sizeof("255.255.255.255") + 1];
+    int norm;
 
     /*
      * Parse the login details and strip them out of the host name.
@@ -1262,21 +1268,28 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
       goto fail;
     }
 
-    if(ipv4_normalize(Curl_dyn_ptr(&host),
-                      normalized_ipv4, sizeof(normalized_ipv4))) {
+    norm = ipv4_normalize(Curl_dyn_ptr(&host),
+                          normalized_ipv4, sizeof(normalized_ipv4));
+    switch(norm) {
+    case IPV4_CLEANED:
       Curl_dyn_reset(&host);
-      if(Curl_dyn_add(&host, normalized_ipv4)) {
+      if(Curl_dyn_add(&host, normalized_ipv4))
         result = CURLUE_OUT_OF_MEMORY;
-        goto fail;
-      }
-    }
-    else {
+      break;
+
+    case IPV4_NOTANIP:
       result = decode_host(&host);
       if(!result)
         result = hostname_check(u, Curl_dyn_ptr(&host), Curl_dyn_len(&host));
-      if(result)
-        goto fail;
+      break;
+
+    case IPV4_BAD:
+    default:
+      result = CURLUE_BAD_HOSTNAME; /* Bad IPv4 address even */
+      break;
     }
+    if(result)
+      goto fail;
 
     if((flags & CURLU_GUESS_SCHEME) && !schemep) {
       const char *hostname = Curl_dyn_ptr(&host);
index a16d16adf5c2a4d573d29aeb70e4ff5023e0b8ed..aba084162aa4e2acaeda72c8a2f986d65b9325d8 100644 (file)
@@ -531,13 +531,14 @@ static const struct urltestcase get_url_list[] = {
   {"https://a127.0.0.1", "https://a127.0.0.1/", 0, 0, CURLUE_OK},
   {"https://\xff.127.0.0.1", "https://%FF.127.0.0.1/", 0, CURLU_URLENCODE,
    CURLUE_OK},
-  {"https://127.-0.0.1", "https://127.-0.0.1/", 0, 0, CURLUE_OK},
+  {"https://127.-0.0.1", "https://127.-0.0.1/", 0, 0, CURLUE_BAD_HOSTNAME},
   {"https://127.0. 1", "https://127.0.0.1/", 0, 0, CURLUE_BAD_HOSTNAME},
-  {"https://1.0x1000000", "https://1.0x1000000/", 0, 0, CURLUE_OK},
-  {"https://1.2.3.256", "https://1.2.3.256/", 0, 0, CURLUE_OK},
-  {"https://1.2.3.4.5", "https://1.2.3.4.5/", 0, 0, CURLUE_OK},
-  {"https://1.2.0x100.3", "https://1.2.0x100.3/", 0, 0, CURLUE_OK},
-  {"https://4294967296", "https://4294967296/", 0, 0, CURLUE_OK},
+  {"https://1.0x1000000", "https://1.0x1000000/", 0, 0, CURLUE_BAD_HOSTNAME},
+  {"https://1.2.3.256", "https://1.2.3.256/", 0, 0, CURLUE_BAD_HOSTNAME},
+  {"https://1.2.3.4.5", "https://1.2.3.4.5/", 0, 0, CURLUE_BAD_HOSTNAME},
+  {"https://1.2.0x100.3", "https://1.2.0x100.3/", 0, 0, CURLUE_BAD_HOSTNAME},
+  {"https://4294967296", "https://4294967296/", 0, 0, CURLUE_BAD_HOSTNAME},
+  {"https://123host", "https://123host/", 0, 0, CURLUE_OK},
   /* 40 bytes scheme is the max allowed */
   {"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA://hostname/path",
    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa://hostname/path",