]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Fix CVE-2022-3786 in punycode decoder.
authorPauli <pauli@openssl.org>
Wed, 26 Oct 2022 23:22:47 +0000 (10:22 +1100)
committerTomas Mraz <tomas@openssl.org>
Tue, 1 Nov 2022 16:44:49 +0000 (17:44 +0100)
Fixed the ossl_a2ulabel() function which also contained a potential
buffer overflow, albeit without control of the contents.
This overflow could result in a crash (causing a denial of service).

The function also did not NUL-terminate the output in some cases.

The two issues fixed here were dentified and reported
by Viktor Dukhovni while researching CVE-2022-3602.

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(cherry picked from commit c42165b5706e42f67ef8ef4c351a9a4c5d21639a)

crypto/punycode.c

index f0796163e3e94d9dfc63b3638a6ec9203a779fcd..f55e351f7a5c35d00cef27658e92aae86a8c8f67 100644 (file)
@@ -123,7 +123,6 @@ int ossl_punycode_decode(const char *pEncoded, const size_t enc_len,
     unsigned int bias = initial_bias;
     size_t processed_in = 0, written_out = 0;
     unsigned int max_out = *pout_length;
-
     unsigned int basic_count = 0;
     unsigned int loop;
 
@@ -185,7 +184,7 @@ int ossl_punycode_decode(const char *pEncoded, const size_t enc_len,
             return 0;
 
         memmove(pDecoded + i + 1, pDecoded + i,
-                (written_out - i) * sizeof *pDecoded);
+                (written_out - i) * sizeof(*pDecoded));
         pDecoded[i] = n;
         i++;
         written_out++;
@@ -255,30 +254,35 @@ int ossl_a2ulabel(const char *in, char *out, size_t *outlen)
      */
     char *outptr = out;
     const char *inptr = in;
-    size_t size = 0;
+    size_t size = 0, maxsize;
     int result = 1;
-
+    unsigned int i, j;
     unsigned int buf[LABEL_BUF_SIZE];      /* It's a hostname */
-    if (out == NULL)
+
+    if (out == NULL) {
         result = 0;
+        maxsize = 0;
+    } else {
+        maxsize = *outlen;
+    }
+
+#define PUSHC(c)                    \
+    do                              \
+        if (size++ < maxsize)       \
+            *outptr++ = c;          \
+        else                        \
+            result = 0;             \
+    while (0)
 
     while (1) {
         char *tmpptr = strchr(inptr, '.');
-        size_t delta = (tmpptr) ? (size_t)(tmpptr - inptr) : strlen(inptr);
+        size_t delta = tmpptr != NULL ? (size_t)(tmpptr - inptr) : strlen(inptr);
 
         if (!HAS_PREFIX(inptr, "xn--")) {
-            size += delta + 1;
-
-            if (size >= *outlen - 1)
-                result = 0;
-
-            if (result > 0) {
-                memcpy(outptr, inptr, delta + 1);
-                outptr += delta + 1;
-            }
+            for (i = 0; i < delta + 1; i++)
+                PUSHC(inptr[i]);
         } else {
             unsigned int bufsize = LABEL_BUF_SIZE;
-            unsigned int i;
 
             if (ossl_punycode_decode(inptr + 4, delta - 4, buf, &bufsize) <= 0)
                 return -1;
@@ -286,26 +290,15 @@ int ossl_a2ulabel(const char *in, char *out, size_t *outlen)
             for (i = 0; i < bufsize; i++) {
                 unsigned char seed[6];
                 size_t utfsize = codepoint2utf8(seed, buf[i]);
+
                 if (utfsize == 0)
                     return -1;
 
-                size += utfsize;
-                if (size >= *outlen - 1)
-                    result = 0;
-
-                if (result > 0) {
-                    memcpy(outptr, seed, utfsize);
-                    outptr += utfsize;
-                }
+                for (j = 0; j < utfsize; j++)
+                    PUSHC(seed[j]);
             }
 
-            if (tmpptr != NULL) {
-                *outptr = '.';
-                outptr++;
-                size++;
-                if (size >= *outlen - 1)
-                    result = 0;
-            }
+            PUSHC(tmpptr != NULL ? '.' : '\0');
         }
 
         if (tmpptr == NULL)
@@ -313,7 +306,9 @@ int ossl_a2ulabel(const char *in, char *out, size_t *outlen)
 
         inptr = tmpptr + 1;
     }
+#undef PUSHC
 
+    *outlen = size;
     return result;
 }
 
@@ -327,12 +322,11 @@ int ossl_a2ulabel(const char *in, char *out, size_t *outlen)
 
 int ossl_a2ucompare(const char *a, const char *u)
 {
-    char a_ulabel[LABEL_BUF_SIZE];
+    char a_ulabel[LABEL_BUF_SIZE + 1];
     size_t a_size = sizeof(a_ulabel);
 
-    if (ossl_a2ulabel(a, a_ulabel, &a_size) <= 0) {
+    if (ossl_a2ulabel(a, a_ulabel, &a_size) <= 0)
         return -1;
-    }
 
-    return (strcmp(a_ulabel, u) == 0) ? 0 : 1;
+    return strcmp(a_ulabel, u) != 0;
 }