Fix buffer overflow in Public Key Pinning

* src/utils.c (wget_base64_decode): Add param for destination size,
  (wg_pubkey_pem_to_der): Amend call to wget_base64_decode(),
  (wg_pin_peer_pubkey): Likewise and fix code style.
* src/utils.h: Add param to wget_base64_decode()
* src/http-ntlm.c (ntlm_input): Amend call to wget_base64_decode()
* src/http.c (skip_content_type): Likewise

Fixes #51227
This commit is contained in:
Tim Rühsen 2017-06-13 10:23:04 +02:00
parent 407c1f990a
commit ae293c945a
4 changed files with 86 additions and 58 deletions

View File

@ -122,7 +122,7 @@ ntlm_input (struct ntlmdata *ntlm, const char *header)
DEBUGP (("Received a type-2 NTLM message.\n"));
size = wget_base64_decode (header, buffer);
size = wget_base64_decode (header, buffer, strlen (header));
if (size < 0)
return false; /* malformed base64 from server */

View File

@ -2998,7 +2998,7 @@ skip_content_type:
char *bin_hash = alloca (dig_hash_str_len * 3 / 4 + 1);
ssize_t hash_bin_len;
hash_bin_len = wget_base64_decode (dig_hash, bin_hash);
hash_bin_len = wget_base64_decode (dig_hash, bin_hash, dig_hash_str_len * 3 / 4 + 1);
/* Detect malformed base64 input. */
if (hash_bin_len < 0)

View File

@ -2346,7 +2346,7 @@ wget_base64_encode (const void *data, size_t length, char *dest)
This function originates from Free Recode. */
ssize_t
wget_base64_decode (const char *base64, void *dest)
wget_base64_decode (const char *base64, void *dest, size_t size)
{
/* Table of base64 values for first 128 characters. Note that this
assumes ASCII (but so does Wget in other places). */
@ -2370,7 +2370,8 @@ wget_base64_decode (const char *base64, void *dest)
#define IS_BASE64(c) ((IS_ASCII (c) && BASE64_CHAR_TO_VALUE (c) >= 0) || c == '=')
const char *p = base64;
char *q = dest;
unsigned char *q = dest;
ssize_t n = 0;
while (1)
{
@ -2392,7 +2393,12 @@ wget_base64_decode (const char *base64, void *dest)
if (c == '=' || !IS_BASE64 (c))
return -1; /* illegal char while decoding base64 */
value |= BASE64_CHAR_TO_VALUE (c) << 12;
*q++ = value >> 16;
if (size)
{
*q++ = value >> 16;
size--;
}
n++;
/* Process third byte of a quadruplet. */
NEXT_CHAR (c, p);
@ -2412,7 +2418,12 @@ wget_base64_decode (const char *base64, void *dest)
}
value |= BASE64_CHAR_TO_VALUE (c) << 6;
*q++ = 0xff & value >> 8;
if (size)
{
*q++ = 0xff & value >> 8;
size--;
}
n++;
/* Process fourth byte of a quadruplet. */
NEXT_CHAR (c, p);
@ -2424,12 +2435,17 @@ wget_base64_decode (const char *base64, void *dest)
return -1; /* illegal char while decoding base64 */
value |= BASE64_CHAR_TO_VALUE (c);
*q++ = 0xff & value;
if (size)
{
*q++ = 0xff & value;
size--;
}
n++;
}
#undef IS_BASE64
#undef BASE64_CHAR_TO_VALUE
return q - (char *) dest;
return n;
}
#ifdef HAVE_LIBPCRE
@ -2726,7 +2742,7 @@ wg_pubkey_pem_to_der (const char *pem, unsigned char **der, size_t *der_len)
base64data = xmalloc (BASE64_LENGTH(stripped_pem_count));
size = wget_base64_decode (stripped_pem, base64data);
size = wget_base64_decode (stripped_pem, base64data, BASE64_LENGTH(stripped_pem_count));
if (size < 0) {
xfree (base64data); /* malformed base64 from server */
@ -2765,54 +2781,65 @@ wg_pin_peer_pubkey (const char *pinnedpubkey, const char *pubkey, size_t pubkeyl
return result;
/* only do this if pinnedpubkey starts with "sha256//", length 8 */
if (strncmp (pinnedpubkey, "sha256//", 8) == 0) {
/* compute sha256sum of public key */
sha256sumdigest = xmalloc (SHA256_DIGEST_SIZE);
sha256_buffer (pubkey, pubkeylen, sha256sumdigest);
expectedsha256sumdigest = xmalloc (SHA256_DIGEST_SIZE + 1);
if (strncmp (pinnedpubkey, "sha256//", 8) == 0)
{
/* compute sha256sum of public key */
sha256sumdigest = xmalloc (SHA256_DIGEST_SIZE);
sha256_buffer (pubkey, pubkeylen, sha256sumdigest);
expectedsha256sumdigest = xmalloc (SHA256_DIGEST_SIZE);
/* it starts with sha256//, copy so we can modify it */
pinkeylen = strlen (pinnedpubkey) + 1;
pinkeycopy = xmalloc (pinkeylen);
memcpy (pinkeycopy, pinnedpubkey, pinkeylen);
/* it starts with sha256//, copy so we can modify it */
pinkeylen = strlen (pinnedpubkey) + 1;
pinkeycopy = xmalloc (pinkeylen);
memcpy (pinkeycopy, pinnedpubkey, pinkeylen);
/* point begin_pos to the copy, and start extracting keys */
begin_pos = pinkeycopy;
do
{
end_pos = strstr (begin_pos, ";sha256//");
/*
* if there is an end_pos, null terminate,
* otherwise it'll go to the end of the original string
*/
if (end_pos)
end_pos[0] = '\0';
/* point begin_pos to the copy, and start extracting keys */
begin_pos = pinkeycopy;
do
{
end_pos = strstr (begin_pos, ";sha256//");
/*
* if there is an end_pos, null terminate,
* otherwise it'll go to the end of the original string
*/
if (end_pos)
end_pos[0] = '\0';
/* decode base64 pinnedpubkey, 8 is length of "sha256//" */
decoded_hash_length = wget_base64_decode (begin_pos + 8, expectedsha256sumdigest);
/* if valid base64, compare sha256 digests directly */
if (SHA256_DIGEST_SIZE == decoded_hash_length &&
!memcmp (sha256sumdigest, expectedsha256sumdigest, SHA256_DIGEST_SIZE)) {
result = true;
break;
/* decode base64 pinnedpubkey, 8 is length of "sha256//" */
decoded_hash_length = wget_base64_decode (begin_pos + 8, expectedsha256sumdigest, SHA256_DIGEST_SIZE);
/* if valid base64, compare sha256 digests directly */
if (SHA256_DIGEST_SIZE == decoded_hash_length)
{
if (!memcmp (sha256sumdigest, expectedsha256sumdigest, SHA256_DIGEST_SIZE))
{
result = true;
break;
}
}
else
logprintf (LOG_VERBOSE, _ ("Skipping key with wrong size (%d/%d): %s\n"),
(strlen (begin_pos + 8) * 3) / 4, SHA256_DIGEST_SIZE,
quote (begin_pos + 8));
/*
* change back the null-terminator we changed earlier,
* and look for next begin
*/
if (end_pos)
{
end_pos[0] = ';';
begin_pos = strstr (end_pos, "sha256//");
}
}
while (end_pos && begin_pos);
/*
* change back the null-terminator we changed earlier,
* and look for next begin
*/
if (end_pos) {
end_pos[0] = ';';
begin_pos = strstr (end_pos, "sha256//");
}
} while (end_pos && begin_pos);
xfree (sha256sumdigest);
xfree (expectedsha256sumdigest);
xfree (pinkeycopy);
xfree (sha256sumdigest);
xfree (expectedsha256sumdigest);
xfree (pinkeycopy);
return result;
}
return result;
}
/* fall back to assuming this is a file path */
fm = wget_read_file (pinnedpubkey);
@ -2832,11 +2859,12 @@ wg_pin_peer_pubkey (const char *pinnedpubkey, const char *pubkey, size_t pubkeyl
goto cleanup;
/* If the sizes are the same, it can't be base64 encoded, must be der */
if (pubkeylen == size) {
if (!memcmp (pubkey, fm->content, pubkeylen))
result = true;
goto cleanup;
}
if (pubkeylen == size)
{
if (!memcmp (pubkey, fm->content, pubkeylen))
result = true;
goto cleanup;
}
/*
* Otherwise we will assume it's PEM and try to decode it
@ -2858,7 +2886,7 @@ wg_pin_peer_pubkey (const char *pinnedpubkey, const char *pubkey, size_t pubkeyl
if (pubkeylen == pem_len && !memcmp (pubkey, pem_ptr, pubkeylen))
result = true;
cleanup:
cleanup:
xfree (buf);
xfree (pem_ptr);
wget_read_file_free (fm);

View File

@ -149,7 +149,7 @@ void xsleep (double);
#define BASE64_LENGTH(len) (4 * (((len) + 2) / 3))
size_t wget_base64_encode (const void *, size_t, char *);
ssize_t wget_base64_decode (const char *, void *);
ssize_t wget_base64_decode (const char *, void *, size_t);
#ifdef HAVE_LIBPCRE
void *compile_pcre_regex (const char *);