251 lines
8.4 KiB
Diff
251 lines
8.4 KiB
Diff
Created by combining upstream commit 4608619a1cf578f16e799510eaa0a21c0f1f08e3
|
|
that fixes the CVE
|
|
with upstream commit b282280e9b69cae988c0c69cce3eda4d4bd38fff
|
|
that provides the function the fix uses, `timingsafe_bcmp`
|
|
|
|
diff --git a/configure b/configure
|
|
index 91df389..4b85063 100755
|
|
--- a/configure
|
|
+++ b/configure
|
|
@@ -16305,6 +16305,16 @@ fi
|
|
cat >>confdefs.h <<_ACEOF
|
|
#define HAVE_DECL_STRNLEN $ac_have_decl
|
|
_ACEOF
|
|
+ac_fn_c_check_decl "$LINENO" "timingsafe_bcmp" "ac_cv_have_decl_timingsafe_bcmp" "$ac_includes_default"
|
|
+if test "x$ac_cv_have_decl_timingsafe_bcmp" = xyes; then :
|
|
+ ac_have_decl=1
|
|
+else
|
|
+ ac_have_decl=0
|
|
+fi
|
|
+
|
|
+cat >>confdefs.h <<_ACEOF
|
|
+#define HAVE_DECL_TIMINGSAFE_BCMP $ac_have_decl
|
|
+_ACEOF
|
|
|
|
|
|
# We can't use AC_REPLACE_FUNCS to replace these functions, because it
|
|
@@ -16639,6 +16649,19 @@ esac
|
|
|
|
fi
|
|
|
|
+ac_fn_c_check_func "$LINENO" "timingsafe_bcmp" "ac_cv_func_timingsafe_bcmp"
|
|
+if test "x$ac_cv_func_timingsafe_bcmp" = xyes; then :
|
|
+ $as_echo "#define HAVE_TIMINGSAFE_BCMP 1" >>confdefs.h
|
|
+
|
|
+else
|
|
+ case " $LIBOBJS " in
|
|
+ *" timingsafe_bcmp.$ac_objext "* ) ;;
|
|
+ *) LIBOBJS="$LIBOBJS timingsafe_bcmp.$ac_objext"
|
|
+ ;;
|
|
+esac
|
|
+
|
|
+fi
|
|
+
|
|
|
|
|
|
if test "$PORTNAME" = "win32" -o "$PORTNAME" = "cygwin"; then
|
|
diff --git a/configure.in b/configure.in
|
|
index 318851d..5461fc6 100644
|
|
--- a/configure.in
|
|
+++ b/configure.in
|
|
@@ -1793,7 +1793,7 @@ AC_CHECK_DECLS(posix_fadvise, [], [], [#include <fcntl.h>])
|
|
]) # fi
|
|
|
|
AC_CHECK_DECLS(fdatasync, [], [], [#include <unistd.h>])
|
|
-AC_CHECK_DECLS([strlcat, strlcpy, strnlen])
|
|
+AC_CHECK_DECLS([strlcat, strlcpy, strnlen, timingsafe_bcmp])
|
|
|
|
# We can't use AC_REPLACE_FUNCS to replace these functions, because it
|
|
# won't handle deployment target restrictions on macOS
|
|
@@ -1843,6 +1843,7 @@ AC_REPLACE_FUNCS(m4_normalize([
|
|
strlcpy
|
|
strnlen
|
|
strtof
|
|
+ timingsafe_bcmp
|
|
]))
|
|
|
|
if test "$PORTNAME" = "win32" -o "$PORTNAME" = "cygwin"; then
|
|
diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c
|
|
index 5214d32..75c97ca 100644
|
|
--- a/src/backend/libpq/auth-scram.c
|
|
+++ b/src/backend/libpq/auth-scram.c
|
|
@@ -537,7 +537,7 @@ scram_verify_plain_password(const char *username, const char *password,
|
|
* Compare the secret's Server Key with the one computed from the
|
|
* user-supplied password.
|
|
*/
|
|
- return memcmp(computed_key, server_key, SCRAM_KEY_LEN) == 0;
|
|
+ return timingsafe_bcmp(computed_key, server_key, SCRAM_KEY_LEN) == 0;
|
|
}
|
|
|
|
|
|
@@ -1074,9 +1074,9 @@ verify_final_nonce(scram_state *state)
|
|
|
|
if (final_nonce_len != client_nonce_len + server_nonce_len)
|
|
return false;
|
|
- if (memcmp(state->client_final_nonce, state->client_nonce, client_nonce_len) != 0)
|
|
+ if (timingsafe_bcmp(state->client_final_nonce, state->client_nonce, client_nonce_len) != 0)
|
|
return false;
|
|
- if (memcmp(state->client_final_nonce + client_nonce_len, state->server_nonce, server_nonce_len) != 0)
|
|
+ if (timingsafe_bcmp(state->client_final_nonce + client_nonce_len, state->server_nonce, server_nonce_len) != 0)
|
|
return false;
|
|
|
|
return true;
|
|
@@ -1117,7 +1117,7 @@ verify_client_proof(scram_state *state)
|
|
/* Hash it one more time, and compare with StoredKey */
|
|
scram_H(ClientKey, SCRAM_KEY_LEN, client_StoredKey);
|
|
|
|
- if (memcmp(client_StoredKey, state->StoredKey, SCRAM_KEY_LEN) != 0)
|
|
+ if (timingsafe_bcmp(client_StoredKey, state->StoredKey, SCRAM_KEY_LEN) != 0)
|
|
return false;
|
|
|
|
return true;
|
|
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
|
|
index 8b63217..e56eab1 100644
|
|
--- a/src/backend/libpq/auth.c
|
|
+++ b/src/backend/libpq/auth.c
|
|
@@ -3375,7 +3375,7 @@ PerformRadiusTransaction(const char *server, const char *secret, const char *por
|
|
}
|
|
pfree(cryptvector);
|
|
|
|
- if (memcmp(receivepacket->vector, encryptedpassword, RADIUS_VECTOR_LENGTH) != 0)
|
|
+ if (timingsafe_bcmp(receivepacket->vector, encryptedpassword, RADIUS_VECTOR_LENGTH) != 0)
|
|
{
|
|
ereport(LOG,
|
|
(errmsg("RADIUS response from %s has incorrect MD5 signature",
|
|
diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c
|
|
index 17b91ac..7f3aea5 100644
|
|
--- a/src/backend/libpq/crypt.c
|
|
+++ b/src/backend/libpq/crypt.c
|
|
@@ -196,7 +196,8 @@ md5_crypt_verify(const char *role, const char *shadow_pass,
|
|
return STATUS_ERROR;
|
|
}
|
|
|
|
- if (strcmp(client_pass, crypt_pwd) == 0)
|
|
+ if (strlen(client_pass) == strlen(crypt_pwd) &&
|
|
+ timingsafe_bcmp(client_pass, crypt_pwd, strlen(crypt_pwd)) == 0)
|
|
retval = STATUS_OK;
|
|
else
|
|
{
|
|
@@ -261,7 +262,8 @@ plain_crypt_verify(const char *role, const char *shadow_pass,
|
|
*/
|
|
return STATUS_ERROR;
|
|
}
|
|
- if (strcmp(crypt_client_pass, shadow_pass) == 0)
|
|
+ if (strlen(crypt_client_pass) == strlen(shadow_pass) &&
|
|
+ timingsafe_bcmp(crypt_client_pass, shadow_pass, strlen(shadow_pass)) == 0)
|
|
return STATUS_OK;
|
|
else
|
|
{
|
|
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
|
|
index b351a0b..d86ad1d 100644
|
|
--- a/src/include/pg_config.h.in
|
|
+++ b/src/include/pg_config.h.in
|
|
@@ -175,6 +175,10 @@
|
|
don't. */
|
|
#undef HAVE_DECL_STRTOULL
|
|
|
|
+/* Define to 1 if you have the declaration of `timingsafe_bcmp', and to 0 if
|
|
+ you don't. */
|
|
+#undef HAVE_DECL_TIMINGSAFE_BCMP
|
|
+
|
|
/* Define to 1 if you have the `dlopen' function. */
|
|
#undef HAVE_DLOPEN
|
|
|
|
@@ -634,6 +638,9 @@
|
|
/* Define to 1 if you have the <termios.h> header file. */
|
|
#undef HAVE_TERMIOS_H
|
|
|
|
+/* Define to 1 if you have the `timingsafe_bcmp' function. */
|
|
+#undef HAVE_TIMINGSAFE_BCMP
|
|
+
|
|
/* Define to 1 if your compiler understands `typeof' or something similar. */
|
|
#undef HAVE_TYPEOF
|
|
|
|
diff --git a/src/include/port.h b/src/include/port.h
|
|
index 0e86dfb..d3ca64e 100644
|
|
--- a/src/include/port.h
|
|
+++ b/src/include/port.h
|
|
@@ -514,6 +514,10 @@ extern int pqGethostbyname(const char *name,
|
|
struct hostent **result,
|
|
int *herrno);
|
|
|
|
+#if !HAVE_DECL_TIMINGSAFE_BCMP
|
|
+extern int timingsafe_bcmp(const void *b1, const void *b2, size_t len);
|
|
+#endif
|
|
+
|
|
extern void pg_qsort(void *base, size_t nel, size_t elsize,
|
|
int (*cmp) (const void *, const void *));
|
|
extern int pg_qsort_strcmp(const void *a, const void *b);
|
|
diff --git a/src/interfaces/libpq/fe-auth-scram.c b/src/interfaces/libpq/fe-auth-scram.c
|
|
index 3e705f8..065244c 100644
|
|
--- a/src/interfaces/libpq/fe-auth-scram.c
|
|
+++ b/src/interfaces/libpq/fe-auth-scram.c
|
|
@@ -612,7 +612,7 @@ read_server_first_message(fe_scram_state *state, char *input)
|
|
|
|
/* Verify immediately that the server used our part of the nonce */
|
|
if (strlen(nonce) < strlen(state->client_nonce) ||
|
|
- memcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
|
|
+ timingsafe_bcmp(nonce, state->client_nonce, strlen(state->client_nonce)) != 0)
|
|
{
|
|
printfPQExpBuffer(&conn->errorMessage,
|
|
libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
|
|
@@ -814,7 +814,8 @@ verify_server_signature(fe_scram_state *state)
|
|
strlen(state->client_final_message_without_proof));
|
|
scram_HMAC_final(expected_ServerSignature, &ctx);
|
|
|
|
- if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
|
|
+ if (timingsafe_bcmp(expected_ServerSignature, state->ServerSignature,
|
|
+ SCRAM_KEY_LEN) != 0)
|
|
return false;
|
|
|
|
return true;
|
|
diff --git a/src/port/timingsafe_bcmp.c b/src/port/timingsafe_bcmp.c
|
|
new file mode 100644
|
|
index 0000000..288865f
|
|
--- /dev/null
|
|
+++ b/src/port/timingsafe_bcmp.c
|
|
@@ -0,0 +1,43 @@
|
|
+/*
|
|
+ * src/port/timingsafe_bcmp.c
|
|
+ *
|
|
+ * $OpenBSD: timingsafe_bcmp.c,v 1.3 2015/08/31 02:53:57 guenther Exp $
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Copyright (c) 2010 Damien Miller. All rights reserved.
|
|
+ *
|
|
+ * Permission to use, copy, modify, and distribute this software for any
|
|
+ * purpose with or without fee is hereby granted, provided that the above
|
|
+ * copyright notice and this permission notice appear in all copies.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include "c.h"
|
|
+
|
|
+#ifdef USE_SSL
|
|
+#include <openssl/crypto.h>
|
|
+#endif
|
|
+
|
|
+int
|
|
+timingsafe_bcmp(const void *b1, const void *b2, size_t n)
|
|
+{
|
|
+#ifdef USE_SSL
|
|
+ return CRYPTO_memcmp(b1, b2, n);
|
|
+#else
|
|
+ const unsigned char *p1 = b1,
|
|
+ *p2 = b2;
|
|
+ int ret = 0;
|
|
+
|
|
+ for (; n > 0; n--)
|
|
+ ret |= *p1++ ^ *p2++;
|
|
+ return (ret != 0);
|
|
+#endif
|
|
+}
|