import CS git postgresql-13.23-3.el8
This commit is contained in:
parent
13162f68d5
commit
9db80d4efd
1782
SOURCES/postgresql-CVE-2026-6473.patch
Normal file
1782
SOURCES/postgresql-CVE-2026-6473.patch
Normal file
File diff suppressed because it is too large
Load Diff
155
SOURCES/postgresql-CVE-2026-6475.patch
Normal file
155
SOURCES/postgresql-CVE-2026-6475.patch
Normal file
@ -0,0 +1,155 @@
|
||||
From 498829dca45ad207a23954fe2e5a30ed2ef4b363 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Paquier <michael@paquier.xyz>
|
||||
Date: Mon, 11 May 2026 05:13:51 -0700
|
||||
Subject: [PATCH] Prevent path traversal in pg_basebackup and pg_rewind
|
||||
|
||||
pg_rewind and pg_basebackup could be fed paths from rogue endpoints that
|
||||
could overwrite the contents of the client when received, achieving path
|
||||
traversal.
|
||||
|
||||
There were two areas in the tree that were sensitive to this problem:
|
||||
- pg_basebackup, through the astreamer code, where no validation was
|
||||
performed before building an output path when streaming tar data. This
|
||||
is an issue in v15 and newer versions.
|
||||
- pg_rewind file operations for paths received through libpq, for all
|
||||
the stable branches supported.
|
||||
|
||||
In order to address this problem, this commit adds a helper function in
|
||||
path.c, that reuses path_is_relative_and_below_cwd() after applying
|
||||
canonicalize_path(). This can be used to validate the paths received
|
||||
from a connection point. A path is considered invalid if any of the two
|
||||
following conditions is satisfied:
|
||||
- The path is absolute.
|
||||
- The path includes a direct parent-directory reference.
|
||||
|
||||
Reported-by: XlabAI Team of Tencent Xuanwu Lab
|
||||
Reported-by: Valery Gubanov <valerygubanov95@gmail.com>
|
||||
Author: Michael Paquier <michael@paquier.xyz>
|
||||
Reviewed-by: Amit Kapila <amit.kapila16@gmail.com>
|
||||
Backpatch-through: 14
|
||||
Security: CVE-2026-6475
|
||||
---
|
||||
src/bin/pg_rewind/file_ops.c | 23 +++++++++++++++++++++++
|
||||
src/include/port.h | 1 +
|
||||
src/port/path.c | 17 +++++++++++++++++
|
||||
3 files changed, 41 insertions(+)
|
||||
|
||||
diff --git a/src/bin/pg_rewind/file_ops.c b/src/bin/pg_rewind/file_ops.c
|
||||
index a807086..faaa5ce 100644
|
||||
--- a/src/bin/pg_rewind/file_ops.c
|
||||
+++ b/src/bin/pg_rewind/file_ops.c
|
||||
@@ -48,6 +48,9 @@ open_target_file(const char *path, bool trunc)
|
||||
{
|
||||
int mode;
|
||||
|
||||
+ if (!path_is_safe_for_extraction(path))
|
||||
+ pg_fatal("target file path is unsafe for open: \"%s\"", path);
|
||||
+
|
||||
if (dry_run)
|
||||
return;
|
||||
|
||||
@@ -188,6 +191,9 @@ remove_target_file(const char *path, bool missing_ok)
|
||||
{
|
||||
char dstpath[MAXPGPATH];
|
||||
|
||||
+ if (!path_is_safe_for_extraction(path))
|
||||
+ pg_fatal("target file path is unsafe for removal: \"%s\"", path);
|
||||
+
|
||||
if (dry_run)
|
||||
return;
|
||||
|
||||
@@ -208,6 +214,9 @@ truncate_target_file(const char *path, off_t newsize)
|
||||
char dstpath[MAXPGPATH];
|
||||
int fd;
|
||||
|
||||
+ if (!path_is_safe_for_extraction(path))
|
||||
+ pg_fatal("target file path is unsafe for truncation: \"%s\"", path);
|
||||
+
|
||||
if (dry_run)
|
||||
return;
|
||||
|
||||
@@ -230,6 +239,10 @@ create_target_dir(const char *path)
|
||||
{
|
||||
char dstpath[MAXPGPATH];
|
||||
|
||||
+ if (!path_is_safe_for_extraction(path))
|
||||
+ pg_fatal("target directory path is unsafe for directory creation: \"%s\"",
|
||||
+ path);
|
||||
+
|
||||
if (dry_run)
|
||||
return;
|
||||
|
||||
@@ -244,6 +257,10 @@ remove_target_dir(const char *path)
|
||||
{
|
||||
char dstpath[MAXPGPATH];
|
||||
|
||||
+ if (!path_is_safe_for_extraction(path))
|
||||
+ pg_fatal("target directory path is unsafe for directory removal: \"%s\"",
|
||||
+ path);
|
||||
+
|
||||
if (dry_run)
|
||||
return;
|
||||
|
||||
@@ -258,6 +275,9 @@ create_target_symlink(const char *path, const char *link)
|
||||
{
|
||||
char dstpath[MAXPGPATH];
|
||||
|
||||
+ if (!path_is_safe_for_extraction(path))
|
||||
+ pg_fatal("target symlink path is unsafe for creation: \"%s\"", path);
|
||||
+
|
||||
if (dry_run)
|
||||
return;
|
||||
|
||||
@@ -272,6 +292,9 @@ remove_target_symlink(const char *path)
|
||||
{
|
||||
char dstpath[MAXPGPATH];
|
||||
|
||||
+ if (!path_is_safe_for_extraction(path))
|
||||
+ pg_fatal("target symlink path is unsafe for removal: \"%s\"", path);
|
||||
+
|
||||
if (dry_run)
|
||||
return;
|
||||
|
||||
diff --git a/src/include/port.h b/src/include/port.h
|
||||
index db53dec..a257d8d 100644
|
||||
--- a/src/include/port.h
|
||||
+++ b/src/include/port.h
|
||||
@@ -55,6 +55,7 @@ extern void make_native_path(char *path);
|
||||
extern void cleanup_path(char *path);
|
||||
extern bool path_contains_parent_reference(const char *path);
|
||||
extern bool path_is_relative_and_below_cwd(const char *path);
|
||||
+extern bool path_is_safe_for_extraction(const char *path);
|
||||
extern bool path_is_prefix_of_path(const char *path1, const char *path2);
|
||||
extern char *make_absolute_path(const char *path);
|
||||
extern const char *get_progname(const char *argv0);
|
||||
diff --git a/src/port/path.c b/src/port/path.c
|
||||
index d37a484..117f919 100644
|
||||
--- a/src/port/path.c
|
||||
+++ b/src/port/path.c
|
||||
@@ -505,6 +505,23 @@ path_is_relative_and_below_cwd(const char *path)
|
||||
return true;
|
||||
}
|
||||
|
||||
+/*
|
||||
+ * Detect whether a path is safe for use during archive extraction.
|
||||
+ *
|
||||
+ * This applies canonicalize_path(), then it checks that the path does
|
||||
+ * not contain any parent directory references.
|
||||
+ */
|
||||
+bool
|
||||
+path_is_safe_for_extraction(const char *path)
|
||||
+{
|
||||
+ char buf[MAXPGPATH];
|
||||
+
|
||||
+ strlcpy(buf, path, sizeof(buf));
|
||||
+ canonicalize_path(buf);
|
||||
+
|
||||
+ return path_is_relative_and_below_cwd(buf);
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Detect whether path1 is a prefix of path2 (including equality).
|
||||
*
|
||||
--
|
||||
2.39.5 (Apple Git-154)
|
||||
|
||||
219
SOURCES/postgresql-CVE-2026-6477.patch
Normal file
219
SOURCES/postgresql-CVE-2026-6477.patch
Normal file
@ -0,0 +1,219 @@
|
||||
From 2b450648240c6c56f367e74bbf0f785b91f93396 Mon Sep 17 00:00:00 2001
|
||||
From: Petr Khartskhaev <pkhartsk@redhat.com>
|
||||
Date: Wed, 3 Jun 2026 16:34:32 +0200
|
||||
Subject: [PATCH] fix CVE-2026-6477
|
||||
|
||||
---
|
||||
doc/src/sgml/libpq.sgml | 11 +-
|
||||
src/interfaces/libpq/fe-exec.c | 18 +-
|
||||
src/interfaces/libpq/fe-lobj.c | 12 +-
|
||||
src/interfaces/libpq/fe-protocol2.c | 16 +-
|
||||
src/interfaces/libpq/fe-protocol3.c | 14 +-
|
||||
src/interfaces/libpq/libpq-int.h | 9 +-
|
||||
13 files changed, 17098 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
|
||||
index 1a84241..e1f688c 100644
|
||||
--- a/doc/src/sgml/libpq.sgml
|
||||
+++ b/doc/src/sgml/libpq.sgml
|
||||
@@ -5086,15 +5086,20 @@ int PQrequestCancel(PGconn *conn);
|
||||
to send simple function calls to the server.
|
||||
</para>
|
||||
|
||||
- <tip>
|
||||
+ <warning>
|
||||
<para>
|
||||
- This interface is somewhat obsolete, as one can achieve similar
|
||||
+ This interface is unsafe and should not be used. When
|
||||
+ <parameter>result_is_int</parameter> is set to <literal>0</literal>,
|
||||
+ <function>PQfn</function> may write data beyond the end of
|
||||
+ <parameter>result_buf</parameter>, regardless of whether the buffer has
|
||||
+ enough space for the requested number of bytes. Furthermore, it is
|
||||
+ obsolete, as one can achieve similar
|
||||
performance and greater functionality by setting up a prepared
|
||||
statement to define the function call. Then, executing the statement
|
||||
with binary transmission of parameters and results substitutes for a
|
||||
fast-path function call.
|
||||
</para>
|
||||
- </tip>
|
||||
+ </warning>
|
||||
|
||||
<para>
|
||||
The function <function id="libpq-PQfn">PQfn</function><indexterm><primary>PQfn</primary></indexterm>
|
||||
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
|
||||
index b30b565..076a9d0 100644
|
||||
--- a/src/interfaces/libpq/fe-exec.c
|
||||
+++ b/src/interfaces/libpq/fe-exec.c
|
||||
@@ -2665,6 +2665,20 @@ PQfn(PGconn *conn,
|
||||
int result_is_int,
|
||||
const PQArgBlock *args,
|
||||
int nargs)
|
||||
+{
|
||||
+ return PQnfn(conn, fnid, result_buf, -1, result_len,
|
||||
+ result_is_int, args, nargs);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * PQnfn
|
||||
+ * Private version of PQfn() with verification that returned data fits in
|
||||
+ * result_buf when result_is_int == 0. Setting buf_size to -1 disables
|
||||
+ * this verification.
|
||||
+ */
|
||||
+PGresult *
|
||||
+PQnfn(PGconn *conn, int fnid, int *result_buf, int buf_size, int *result_len,
|
||||
+ int result_is_int, const PQArgBlock *args, int nargs)
|
||||
{
|
||||
*result_len = 0;
|
||||
|
||||
@@ -2684,12 +2698,12 @@ PQfn(PGconn *conn,
|
||||
|
||||
if (PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
|
||||
return pqFunctionCall3(conn, fnid,
|
||||
- result_buf, result_len,
|
||||
+ result_buf, buf_size, result_len,
|
||||
result_is_int,
|
||||
args, nargs);
|
||||
else
|
||||
return pqFunctionCall2(conn, fnid,
|
||||
- result_buf, result_len,
|
||||
+ result_buf, buf_size, result_len,
|
||||
result_is_int,
|
||||
args, nargs);
|
||||
}
|
||||
diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c
|
||||
index a94ce06..01e67a3 100644
|
||||
--- a/src/interfaces/libpq/fe-lobj.c
|
||||
+++ b/src/interfaces/libpq/fe-lobj.c
|
||||
@@ -288,8 +288,8 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len)
|
||||
argv[1].len = 4;
|
||||
argv[1].u.integer = (int) len;
|
||||
|
||||
- res = PQfn(conn, conn->lobjfuncs->fn_lo_read,
|
||||
- (void *) buf, &result_len, 0, argv, 2);
|
||||
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_read,
|
||||
+ (void *) buf, len, &result_len, 0, argv, 2);
|
||||
if (PQresultStatus(res) == PGRES_COMMAND_OK)
|
||||
{
|
||||
PQclear(res);
|
||||
@@ -439,8 +439,8 @@ lo_lseek64(PGconn *conn, int fd, pg_int64 offset, int whence)
|
||||
argv[2].len = 4;
|
||||
argv[2].u.integer = whence;
|
||||
|
||||
- res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek64,
|
||||
- (void *) &retval, &result_len, 0, argv, 3);
|
||||
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_lseek64,
|
||||
+ (void *) &retval, sizeof(retval), &result_len, 0, argv, 3);
|
||||
if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8)
|
||||
{
|
||||
PQclear(res);
|
||||
@@ -605,8 +605,8 @@ lo_tell64(PGconn *conn, int fd)
|
||||
argv[0].len = 4;
|
||||
argv[0].u.integer = fd;
|
||||
|
||||
- res = PQfn(conn, conn->lobjfuncs->fn_lo_tell64,
|
||||
- (void *) &retval, &result_len, 0, argv, 1);
|
||||
+ res = PQnfn(conn, conn->lobjfuncs->fn_lo_tell64,
|
||||
+ (void *) &retval, sizeof(retval), &result_len, 0, argv, 1);
|
||||
if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8)
|
||||
{
|
||||
PQclear(res);
|
||||
diff --git a/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c
|
||||
index 9360c54..0c2b629 100644
|
||||
--- a/src/interfaces/libpq/fe-protocol2.c
|
||||
+++ b/src/interfaces/libpq/fe-protocol2.c
|
||||
@@ -1428,7 +1428,7 @@ pqEndcopy2(PGconn *conn)
|
||||
*/
|
||||
PGresult *
|
||||
pqFunctionCall2(PGconn *conn, Oid fnid,
|
||||
- int *result_buf, int *actual_result_len,
|
||||
+ int *result_buf, int buf_size, int *actual_result_len,
|
||||
int result_is_int,
|
||||
const PQArgBlock *args, int nargs)
|
||||
{
|
||||
@@ -1510,6 +1510,20 @@ pqFunctionCall2(PGconn *conn, Oid fnid,
|
||||
}
|
||||
else
|
||||
{
|
||||
+ /*
|
||||
+ * If the server returned too much data for the
|
||||
+ * buffer, something fishy is going on. Abandon ship.
|
||||
+ */
|
||||
+ if (buf_size != -1 && *actual_result_len > buf_size)
|
||||
+ {
|
||||
+ appendPQExpBufferStr(&conn->errorMessage,
|
||||
+ libpq_gettext("server returned too much data\n"));
|
||||
+ conn->asyncStatus = PGASYNC_READY;
|
||||
+ pqDropConnection(conn, true);
|
||||
+ conn->status = CONNECTION_BAD;
|
||||
+ return pqPrepareAsyncResult(conn);
|
||||
+ }
|
||||
+
|
||||
if (pqGetnchar((char *) result_buf,
|
||||
*actual_result_len,
|
||||
conn))
|
||||
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
|
||||
index 3da1ace..a092010 100644
|
||||
--- a/src/interfaces/libpq/fe-protocol3.c
|
||||
+++ b/src/interfaces/libpq/fe-protocol3.c
|
||||
@@ -1903,7 +1903,7 @@ pqEndcopy3(PGconn *conn)
|
||||
*/
|
||||
PGresult *
|
||||
pqFunctionCall3(PGconn *conn, Oid fnid,
|
||||
- int *result_buf, int *actual_result_len,
|
||||
+ int *result_buf, int buf_size, int *actual_result_len,
|
||||
int result_is_int,
|
||||
const PQArgBlock *args, int nargs)
|
||||
{
|
||||
@@ -2034,6 +2034,18 @@ pqFunctionCall3(PGconn *conn, Oid fnid,
|
||||
}
|
||||
else
|
||||
{
|
||||
+ /*
|
||||
+ * If the server returned too much data for the
|
||||
+ * buffer, something fishy is going on. Abandon ship.
|
||||
+ */
|
||||
+ if (buf_size != -1 && *actual_result_len > buf_size)
|
||||
+ {
|
||||
+ appendPQExpBufferStr(&conn->errorMessage,
|
||||
+ libpq_gettext("server returned too much data\n"));
|
||||
+ handleSyncLoss(conn, id, *actual_result_len);
|
||||
+ return pqPrepareAsyncResult(conn);
|
||||
+ }
|
||||
+
|
||||
if (pqGetnchar((char *) result_buf,
|
||||
*actual_result_len,
|
||||
conn))
|
||||
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
|
||||
index 814884a..1b0cf91 100644
|
||||
--- a/src/interfaces/libpq/libpq-int.h
|
||||
+++ b/src/interfaces/libpq/libpq-int.h
|
||||
@@ -622,6 +622,9 @@ extern void pqSaveMessageField(PGresult *res, char code,
|
||||
extern void pqSaveParameterStatus(PGconn *conn, const char *name,
|
||||
const char *value);
|
||||
extern int pqRowProcessor(PGconn *conn, const char **errmsgp);
|
||||
+extern PGresult *PQnfn(PGconn *conn, int fnid, int *result_buf, int buf_size,
|
||||
+ int *result_len, int result_is_int,
|
||||
+ const PQArgBlock *args, int nargs);
|
||||
|
||||
/* === in fe-protocol2.c === */
|
||||
|
||||
@@ -635,7 +638,8 @@ extern int pqGetline2(PGconn *conn, char *s, int maxlen);
|
||||
extern int pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize);
|
||||
extern int pqEndcopy2(PGconn *conn);
|
||||
extern PGresult *pqFunctionCall2(PGconn *conn, Oid fnid,
|
||||
- int *result_buf, int *actual_result_len,
|
||||
+ int *result_buf, int buf_size,
|
||||
+ int *actual_result_len,
|
||||
int result_is_int,
|
||||
const PQArgBlock *args, int nargs);
|
||||
|
||||
@@ -652,7 +656,8 @@ extern int pqGetline3(PGconn *conn, char *s, int maxlen);
|
||||
extern int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize);
|
||||
extern int pqEndcopy3(PGconn *conn);
|
||||
extern PGresult *pqFunctionCall3(PGconn *conn, Oid fnid,
|
||||
- int *result_buf, int *actual_result_len,
|
||||
+ int *result_buf, int buf_size,
|
||||
+ int *actual_result_len,
|
||||
int result_is_int,
|
||||
const PQArgBlock *args, int nargs);
|
||||
|
||||
250
SOURCES/postgresql-CVE-2026-6478.patch
Normal file
250
SOURCES/postgresql-CVE-2026-6478.patch
Normal file
@ -0,0 +1,250 @@
|
||||
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
|
||||
+}
|
||||
203
SOURCES/postgresql-CVE-2026-6637.patch
Normal file
203
SOURCES/postgresql-CVE-2026-6637.patch
Normal file
@ -0,0 +1,203 @@
|
||||
From 2b026df29c1e1caafb259a2021528a28ec484018 Mon Sep 17 00:00:00 2001
|
||||
From: Nathan Bossart <nathan@postgresql.org>
|
||||
Date: Mon, 11 May 2026 05:13:52 -0700
|
||||
Subject: [PATCH] refint: Fix SQL injection and buffer overruns.
|
||||
|
||||
Maliciously crafted key value updates could achieve SQL injection
|
||||
within check_foreign_key(). To fix, ensure new key values are
|
||||
properly quoted and escaped in the internally generated SQL
|
||||
statements. While at it, avoid potential buffer overruns by
|
||||
replacing the stack buffers for internally generated SQL statements
|
||||
with StringInfo.
|
||||
|
||||
Reported-by: Nikolay Samokhvalov <nik@postgres.ai>
|
||||
Author: Nathan Bossart <nathandbossart@gmail.com>
|
||||
Reviewed-by: Noah Misch <noah@leadboat.com>
|
||||
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
|
||||
Reviewed-by: Fujii Masao <masao.fujii@gmail.com>
|
||||
Security: CVE-2026-6637
|
||||
Backpatch-through: 14
|
||||
---
|
||||
contrib/spi/refint.c | 84 ++++++++++++++++++++------------------------
|
||||
1 file changed, 38 insertions(+), 46 deletions(-)
|
||||
|
||||
diff --git a/contrib/spi/refint.c b/contrib/spi/refint.c
|
||||
index 6fbfef2..cbef463 100644
|
||||
--- a/contrib/spi/refint.c
|
||||
+++ b/contrib/spi/refint.c
|
||||
@@ -166,21 +166,24 @@ check_primary_key(PG_FUNCTION_ARGS)
|
||||
if (plan->nplans <= 0)
|
||||
{
|
||||
SPIPlanPtr pplan;
|
||||
- char sql[8192];
|
||||
+ StringInfoData sql;
|
||||
+
|
||||
+ initStringInfo(&sql);
|
||||
|
||||
/*
|
||||
* Construct query: SELECT 1 FROM _referenced_relation_ WHERE Pkey1 =
|
||||
* $1 [AND Pkey2 = $2 [...]]
|
||||
*/
|
||||
- snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
|
||||
- for (i = 0; i < nkeys; i++)
|
||||
+ appendStringInfo(&sql, "select 1 from %s where ", relname);
|
||||
+ for (i = 1; i <= nkeys; i++)
|
||||
{
|
||||
- snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
|
||||
- args[i + nkeys + 1], i + 1, (i < nkeys - 1) ? "and " : "");
|
||||
+ appendStringInfo(&sql, "%s = $%d ", args[i + nkeys], i);
|
||||
+ if (i < nkeys)
|
||||
+ appendStringInfoString(&sql, "and ");
|
||||
}
|
||||
|
||||
/* Prepare plan for query */
|
||||
- pplan = SPI_prepare(sql, nkeys, argtypes);
|
||||
+ pplan = SPI_prepare(sql.data, nkeys, argtypes);
|
||||
if (pplan == NULL)
|
||||
/* internal error */
|
||||
elog(ERROR, "check_primary_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
|
||||
@@ -196,6 +199,8 @@ check_primary_key(PG_FUNCTION_ARGS)
|
||||
sizeof(SPIPlanPtr));
|
||||
*(plan->splan) = pplan;
|
||||
plan->nplans = 1;
|
||||
+
|
||||
+ pfree(sql.data);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -416,7 +421,6 @@ check_foreign_key(PG_FUNCTION_ARGS)
|
||||
if (plan->nplans <= 0)
|
||||
{
|
||||
SPIPlanPtr pplan;
|
||||
- char sql[8192];
|
||||
char **args2 = args;
|
||||
|
||||
plan->splan = (SPIPlanPtr *) MemoryContextAlloc(TopMemoryContext,
|
||||
@@ -424,6 +428,10 @@ check_foreign_key(PG_FUNCTION_ARGS)
|
||||
|
||||
for (r = 0; r < nrefs; r++)
|
||||
{
|
||||
+ StringInfoData sql;
|
||||
+
|
||||
+ initStringInfo(&sql);
|
||||
+
|
||||
relname = args2[0];
|
||||
|
||||
/*---------
|
||||
@@ -437,8 +445,7 @@ check_foreign_key(PG_FUNCTION_ARGS)
|
||||
*---------
|
||||
*/
|
||||
if (action == 'r')
|
||||
-
|
||||
- snprintf(sql, sizeof(sql), "select 1 from %s where ", relname);
|
||||
+ appendStringInfo(&sql, "select 1 from %s where ", relname);
|
||||
|
||||
/*---------
|
||||
* For 'C'ascade action we construct DELETE query
|
||||
@@ -465,43 +472,24 @@ check_foreign_key(PG_FUNCTION_ARGS)
|
||||
char *nv;
|
||||
int k;
|
||||
|
||||
- snprintf(sql, sizeof(sql), "update %s set ", relname);
|
||||
+ appendStringInfo(&sql, "update %s set ", relname);
|
||||
for (k = 1; k <= nkeys; k++)
|
||||
{
|
||||
- int is_char_type = 0;
|
||||
- char *type;
|
||||
-
|
||||
fn = SPI_fnumber(tupdesc, args_temp[k - 1]);
|
||||
Assert(fn > 0); /* already checked above */
|
||||
nv = SPI_getvalue(newtuple, tupdesc, fn);
|
||||
- type = SPI_gettype(tupdesc, fn);
|
||||
-
|
||||
- if (strcmp(type, "text") == 0 ||
|
||||
- strcmp(type, "varchar") == 0 ||
|
||||
- strcmp(type, "char") == 0 ||
|
||||
- strcmp(type, "bpchar") == 0 ||
|
||||
- strcmp(type, "date") == 0 ||
|
||||
- strcmp(type, "timestamp") == 0)
|
||||
- is_char_type = 1;
|
||||
-#ifdef DEBUG_QUERY
|
||||
- elog(DEBUG4, "check_foreign_key Debug value %s type %s %d",
|
||||
- nv, type, is_char_type);
|
||||
-#endif
|
||||
|
||||
- /*
|
||||
- * is_char_type =1 i set ' ' for define a new value
|
||||
- */
|
||||
- snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
|
||||
- " %s = %s%s%s %s ",
|
||||
- args2[k], (is_char_type > 0) ? "'" : "",
|
||||
- nv, (is_char_type > 0) ? "'" : "", (k < nkeys) ? ", " : "");
|
||||
+ appendStringInfo(&sql, " %s = %s ",
|
||||
+ args2[k], quote_literal_cstr(nv));
|
||||
+ if (k < nkeys)
|
||||
+ appendStringInfoString(&sql, ", ");
|
||||
}
|
||||
- strcat(sql, " where ");
|
||||
+ appendStringInfoString(&sql, " where ");
|
||||
|
||||
}
|
||||
else
|
||||
/* DELETE */
|
||||
- snprintf(sql, sizeof(sql), "delete from %s where ", relname);
|
||||
+ appendStringInfo(&sql, "delete from %s where ", relname);
|
||||
|
||||
}
|
||||
|
||||
@@ -513,25 +501,26 @@ check_foreign_key(PG_FUNCTION_ARGS)
|
||||
*/
|
||||
else if (action == 's')
|
||||
{
|
||||
- snprintf(sql, sizeof(sql), "update %s set ", relname);
|
||||
+ appendStringInfo(&sql, "update %s set ", relname);
|
||||
for (i = 1; i <= nkeys; i++)
|
||||
{
|
||||
- snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql),
|
||||
- "%s = null%s",
|
||||
- args2[i], (i < nkeys) ? ", " : "");
|
||||
+ appendStringInfo(&sql, "%s = null", args2[i]);
|
||||
+ if (i < nkeys)
|
||||
+ appendStringInfoString(&sql, ", ");
|
||||
}
|
||||
- strcat(sql, " where ");
|
||||
+ appendStringInfoString(&sql, " where ");
|
||||
}
|
||||
|
||||
/* Construct WHERE qual */
|
||||
for (i = 1; i <= nkeys; i++)
|
||||
{
|
||||
- snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%s = $%d %s",
|
||||
- args2[i], i, (i < nkeys) ? "and " : "");
|
||||
+ appendStringInfo(&sql, "%s = $%d ", args2[i], i);
|
||||
+ if (i < nkeys)
|
||||
+ appendStringInfoString(&sql, "and ");
|
||||
}
|
||||
|
||||
/* Prepare plan for query */
|
||||
- pplan = SPI_prepare(sql, nkeys, argtypes);
|
||||
+ pplan = SPI_prepare(sql.data, nkeys, argtypes);
|
||||
if (pplan == NULL)
|
||||
/* internal error */
|
||||
elog(ERROR, "check_foreign_key: SPI_prepare returned %s", SPI_result_code_string(SPI_result));
|
||||
@@ -547,11 +536,14 @@ check_foreign_key(PG_FUNCTION_ARGS)
|
||||
plan->splan[r] = pplan;
|
||||
|
||||
args2 += nkeys + 1; /* to the next relation */
|
||||
+
|
||||
+#ifdef DEBUG_QUERY
|
||||
+ elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql.data);
|
||||
+#endif
|
||||
+
|
||||
+ pfree(sql.data);
|
||||
}
|
||||
plan->nplans = nrefs;
|
||||
-#ifdef DEBUG_QUERY
|
||||
- elog(DEBUG4, "check_foreign_key Debug Query is : %s ", sql);
|
||||
-#endif
|
||||
}
|
||||
|
||||
/*
|
||||
--
|
||||
2.39.5 (Apple Git-154)
|
||||
|
||||
@ -60,7 +60,7 @@ Summary: PostgreSQL client programs
|
||||
Name: postgresql
|
||||
%global majorversion 13
|
||||
Version: %{majorversion}.23
|
||||
Release: 2%{?dist}
|
||||
Release: 3%{?dist}
|
||||
|
||||
# The PostgreSQL license is very similar to other MIT licenses, but the OSI
|
||||
# recognizes it as an independent license, so we do as well.
|
||||
@ -108,10 +108,17 @@ Patch6: postgresql-man.patch
|
||||
Patch8: postgresql-external-libpq.patch
|
||||
Patch9: postgresql-server-pg_config.patch
|
||||
Patch10: CVE-2026-2004--CVE-2026-2005--CVE-2026-2006.patch
|
||||
Patch15: postgresql-CVE-2026-6478.patch
|
||||
Patch16: postgresql-CVE-2026-6637.patch
|
||||
Patch17: postgresql-CVE-2026-6477.patch
|
||||
Patch18: postgresql-CVE-2026-6475.patch
|
||||
Patch19: postgresql-CVE-2026-6473.patch
|
||||
|
||||
BuildRequires: gcc
|
||||
BuildRequires: perl(ExtUtils::MakeMaker) glibc-devel bison flex gawk
|
||||
BuildRequires: perl(ExtUtils::Embed), perl-devel
|
||||
BuildRequires: perl(Opcode)
|
||||
BuildRequires: perl(FindBin)
|
||||
%if 0%{?fedora} || 0%{?rhel} > 7
|
||||
BuildRequires: perl-generators
|
||||
%endif
|
||||
@ -119,6 +126,7 @@ BuildRequires: readline-devel zlib-devel
|
||||
BuildRequires: systemd systemd-devel util-linux
|
||||
BuildRequires: multilib-rpm-config
|
||||
BuildRequires: libpq-devel
|
||||
BuildRequires: docbook-style-xsl
|
||||
|
||||
# postgresql-setup build requires
|
||||
BuildRequires: m4 elinks docbook-utils help2man
|
||||
@ -369,6 +377,11 @@ benchmarks.
|
||||
%patch8 -p1
|
||||
%patch9 -p1
|
||||
%patch10 -p1
|
||||
%patch15 -p1
|
||||
%patch16 -p1
|
||||
%patch17 -p1
|
||||
%patch18 -p1
|
||||
%patch19 -p1
|
||||
|
||||
# We used to run autoconf here, but there's no longer any real need to,
|
||||
# since Postgres ships with a reasonably modern configure script.
|
||||
@ -1226,6 +1239,11 @@ make -C postgresql-setup-%{setup_version} check
|
||||
|
||||
|
||||
%changelog
|
||||
* Wed Jun 3 2026 Petr Khartskhaev <pkhartsk@redhat.com> - 13.23-3
|
||||
- Backport fix for CVE-2026-6478 from PostgreSQL 14.23
|
||||
- Backport fixes for CVE-2026-6637, CVE-2026-6477, CVE-2026-6475, CVE-2026-6473
|
||||
- Resolves: RHEL-179806
|
||||
|
||||
* Wed Feb 25 2026 Filip Janus <fjanus@redhat.com> - 13.23-2
|
||||
- fix CVE-2026-2004 CVE-2026-2005 CVE-2026-2006
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user