Fix CVE-2022-3786 in punycode decoder.

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 c42165b570)
This commit is contained in:
Pauli 2022-10-27 10:22:47 +11:00 committed by Tomas Mraz
parent 3b421ebc64
commit 680e65b94c

View 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;
}