using explicit_bzero
In a recent thread[0]/messages/by-id/043403c2-f04d-3a69-aa8a-9bb7b9ce8e5b@iki.fi, the existence of explicit_bzero() was mentioned.
I went to look where we could use that to clear sensitive information
from memory and found a few candidates:
- In be-secure-common.c, clear the entered SSL passphrase in the error
path. (In the non-error path, the buffer belongs to OpenSSL.)
- In libpq, clean up after reading .pgpass. Otherwise, the entire file
including all passwords potentially remains in memory.
- In libpq, clear the password after a connection is closed
(freePGconn/part of PQfinish).
- pg_hba.conf could potentially contain passwords for LDAP, so that
should maybe also be cleared, but the structure of that code would make
that more involved, so I skipped that for now. Efforts are probably
better directed at providing facilities to avoid having to do that.[1]/messages/by-id/CA+hUKGJ44ssWhcKP1KYK2Dm9_XXk1_b629_qSDUhH1fWfuAvXg@mail.gmail.com
Any other ones?
A patch that implements the first three is attached.
[0]: /messages/by-id/043403c2-f04d-3a69-aa8a-9bb7b9ce8e5b@iki.fi
/messages/by-id/043403c2-f04d-3a69-aa8a-9bb7b9ce8e5b@iki.fi
[1]: /messages/by-id/CA+hUKGJ44ssWhcKP1KYK2Dm9_XXk1_b629_qSDUhH1fWfuAvXg@mail.gmail.com
/messages/by-id/CA+hUKGJ44ssWhcKP1KYK2Dm9_XXk1_b629_qSDUhH1fWfuAvXg@mail.gmail.com
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
0001-Use-explicit_bzero.patchtext/plain; charset=UTF-8; name=0001-Use-explicit_bzero.patch; x-mac-creator=0; x-mac-type=0Download
From 5cd9ed8e0cc5fae18657acb033bbdb6fcba90f1b Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Wed, 12 Jun 2019 08:58:22 +0000
Subject: [PATCH] Use explicit_bzero
---
configure | 2 +-
configure.in | 1 +
src/backend/libpq/be-secure-common.c | 1 +
src/include/pg_config.h.in | 3 +++
src/include/port.h | 4 ++++
src/interfaces/libpq/fe-connect.c | 8 ++++++++
6 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/configure b/configure
index fd61bf6472..3fa8ec0603 100755
--- a/configure
+++ b/configure
@@ -15176,7 +15176,7 @@ fi
LIBS_including_readline="$LIBS"
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
-for ac_func in cbrt clock_gettime copyfile fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l
+for ac_func in cbrt clock_gettime copyfile explicit_bzero fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
diff --git a/configure.in b/configure.in
index 4586a1716c..7488f4519f 100644
--- a/configure.in
+++ b/configure.in
@@ -1615,6 +1615,7 @@ AC_CHECK_FUNCS(m4_normalize([
cbrt
clock_gettime
copyfile
+ explicit_bzero
fdatasync
getifaddrs
getpeerucred
diff --git a/src/backend/libpq/be-secure-common.c b/src/backend/libpq/be-secure-common.c
index 877226d377..4c1c6cb3c4 100644
--- a/src/backend/libpq/be-secure-common.c
+++ b/src/backend/libpq/be-secure-common.c
@@ -118,6 +118,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
buf[--len] = '\0';
error:
+ explicit_bzero(buf, size);
pfree(command.data);
return len;
}
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 6cd4cfed0a..0062a4a426 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -201,6 +201,9 @@
/* Define to 1 if you have the <editline/readline.h> header file. */
#undef HAVE_EDITLINE_READLINE_H
+/* Define to 1 if you have the `explicit_bzero' function. */
+#undef HAVE_EXPLICIT_BZERO
+
/* Define to 1 if you have the `fdatasync' function. */
#undef HAVE_FDATASYNC
diff --git a/src/include/port.h b/src/include/port.h
index b5c03d912b..af754b261c 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -381,6 +381,10 @@ extern int isinf(double x);
#endif /* __clang__ && !__cplusplus */
#endif /* !HAVE_ISINF */
+#ifndef HAVE_EXPLICIT_BZERO
+#define explicit_bzero(b, len) bzero(b, len)
+#endif
+
#ifndef HAVE_STRTOF
extern float strtof(const char *nptr, char **endptr);
#endif
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index e58fa6742a..67ea169397 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -3886,7 +3886,10 @@ freePGconn(PGconn *conn)
if (conn->connhost[i].port != NULL)
free(conn->connhost[i].port);
if (conn->connhost[i].password != NULL)
+ {
+ explicit_bzero(conn->connhost[i].password, strlen(conn->connhost[i].password));
free(conn->connhost[i].password);
+ }
}
free(conn->connhost);
}
@@ -3920,7 +3923,10 @@ freePGconn(PGconn *conn)
if (conn->pguser)
free(conn->pguser);
if (conn->pgpass)
+ {
+ explicit_bzero(conn->pgpass, strlen(conn->pgpass));
free(conn->pgpass);
+ }
if (conn->pgpassfile)
free(conn->pgpassfile);
if (conn->keepalives)
@@ -6939,6 +6945,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
if (!ret)
{
/* Out of memory. XXX: an error message would be nice. */
+ explicit_bzero(buf, sizeof(buf));
return NULL;
}
@@ -6955,6 +6962,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
}
fclose(fp);
+ explicit_bzero(buf, sizeof(buf));
return NULL;
#undef LINELEN
--
2.22.0
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
+#ifndef HAVE_EXPLICIT_BZERO +#define explicit_bzero(b, len) bzero(b, len) +#endif
This presumes that every platform has bzero, which is unsafe (POSIX
doesn't specify it) and is an assumption we kicked to the curb a dozen
years ago (067a5cdb3). Please use memset() for the substitute instead.
Also, I'm a bit suspicious of using AC_CHECK_FUNCS for this; that
generally Doesn't Work for anything that's not a vanilla out-of-line
function. Are we worried about people implementing this as a macro,
compiler built-in, etc?
regards, tom lane
Tom Lane <tgl@sss.pgh.pa.us> writes:
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
+#ifndef HAVE_EXPLICIT_BZERO +#define explicit_bzero(b, len) bzero(b, len) +#endifThis presumes that every platform has bzero, which is unsafe (POSIX
doesn't specify it) and is an assumption we kicked to the curb a dozen
years ago (067a5cdb3). Please use memset() for the substitute instead.Also, I'm a bit suspicious of using AC_CHECK_FUNCS for this; that
generally Doesn't Work for anything that's not a vanilla out-of-line
function. Are we worried about people implementing this as a macro,
compiler built-in, etc?
Also, on Linux it requires libbsd: https://libbsd.freedesktop.org/
(which seems to be down, but
https://packages.debian.org/buster/libbsd-dev has a list of the
functions it provides).
- ilmari
--
"A disappointingly low fraction of the human race is,
at any given time, on fire." - Stig Sandbeck Mathisen
ilmari@ilmari.org (Dagfinn Ilmari =?utf-8?Q?Manns=C3=A5ker?=) writes:
Also, on Linux it requires libbsd: https://libbsd.freedesktop.org/
(which seems to be down, but
https://packages.debian.org/buster/libbsd-dev has a list of the
functions it provides).
Ugh, that could be a bit nasty. I might be misremembering, but
my hindbrain is running for cover and yelling something about how
importing libbsd changes signal semantics. Our git log has a few
scary references to other bad side-effects of -lbsd (cf 55c235b26,
1337751e5, a27fafecc). On the whole, I'm not excited about pulling
in a library whose entire purpose is to mess with POSIX semantics.
regards, tom lane
On 2019-06-21 15:25, Tom Lane wrote:
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
+#ifndef HAVE_EXPLICIT_BZERO +#define explicit_bzero(b, len) bzero(b, len) +#endifThis presumes that every platform has bzero, which is unsafe (POSIX
doesn't specify it) and is an assumption we kicked to the curb a dozen
years ago (067a5cdb3). Please use memset() for the substitute instead.
OK, done.
Also, I'm a bit suspicious of using AC_CHECK_FUNCS for this; that
generally Doesn't Work for anything that's not a vanilla out-of-line
function. Are we worried about people implementing this as a macro,
compiler built-in, etc?
I think we should address that if we actually find such a case.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 2019-06-21 15:45, Dagfinn Ilmari Mannsåker wrote:
Also, on Linux it requires libbsd: https://libbsd.freedesktop.org/
No, it's in glibc.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 2019-06-23 21:55, Peter Eisentraut wrote:
On 2019-06-21 15:25, Tom Lane wrote:
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
+#ifndef HAVE_EXPLICIT_BZERO +#define explicit_bzero(b, len) bzero(b, len) +#endifThis presumes that every platform has bzero, which is unsafe (POSIX
doesn't specify it) and is an assumption we kicked to the curb a dozen
years ago (067a5cdb3). Please use memset() for the substitute instead.OK, done.
and with patch attached
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
v2-0001-Use-explicit_bzero.patchtext/plain; charset=UTF-8; name=v2-0001-Use-explicit_bzero.patch; x-mac-creator=0; x-mac-type=0Download
From e193fbcf04833de829069b5e737a27b83c667703 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Sun, 23 Jun 2019 21:53:14 +0200
Subject: [PATCH v2] Use explicit_bzero
---
configure | 2 +-
configure.in | 1 +
src/backend/libpq/be-secure-common.c | 1 +
src/include/pg_config.h.in | 3 +++
src/include/port.h | 4 ++++
src/interfaces/libpq/fe-connect.c | 8 ++++++++
6 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/configure b/configure
index 8d47071e4a..c88153fff5 100755
--- a/configure
+++ b/configure
@@ -15176,7 +15176,7 @@ fi
LIBS_including_readline="$LIBS"
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
-for ac_func in cbrt clock_gettime copyfile fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l
+for ac_func in cbrt clock_gettime copyfile explicit_bzero fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
diff --git a/configure.in b/configure.in
index 74938d4190..7e96d1d419 100644
--- a/configure.in
+++ b/configure.in
@@ -1615,6 +1615,7 @@ AC_CHECK_FUNCS(m4_normalize([
cbrt
clock_gettime
copyfile
+ explicit_bzero
fdatasync
getifaddrs
getpeerucred
diff --git a/src/backend/libpq/be-secure-common.c b/src/backend/libpq/be-secure-common.c
index 877226d377..4c1c6cb3c4 100644
--- a/src/backend/libpq/be-secure-common.c
+++ b/src/backend/libpq/be-secure-common.c
@@ -118,6 +118,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
buf[--len] = '\0';
error:
+ explicit_bzero(buf, size);
pfree(command.data);
return len;
}
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 6cd4cfed0a..0062a4a426 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -201,6 +201,9 @@
/* Define to 1 if you have the <editline/readline.h> header file. */
#undef HAVE_EDITLINE_READLINE_H
+/* Define to 1 if you have the `explicit_bzero' function. */
+#undef HAVE_EXPLICIT_BZERO
+
/* Define to 1 if you have the `fdatasync' function. */
#undef HAVE_FDATASYNC
diff --git a/src/include/port.h b/src/include/port.h
index b5c03d912b..7c8b5138ba 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -381,6 +381,10 @@ extern int isinf(double x);
#endif /* __clang__ && !__cplusplus */
#endif /* !HAVE_ISINF */
+#ifndef HAVE_EXPLICIT_BZERO
+#define explicit_bzero(b, len) memset(b, 0, len)
+#endif
+
#ifndef HAVE_STRTOF
extern float strtof(const char *nptr, char **endptr);
#endif
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index c800d7921e..887b8f6775 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -3884,7 +3884,10 @@ freePGconn(PGconn *conn)
if (conn->connhost[i].port != NULL)
free(conn->connhost[i].port);
if (conn->connhost[i].password != NULL)
+ {
+ explicit_bzero(conn->connhost[i].password, strlen(conn->connhost[i].password));
free(conn->connhost[i].password);
+ }
}
free(conn->connhost);
}
@@ -3918,7 +3921,10 @@ freePGconn(PGconn *conn)
if (conn->pguser)
free(conn->pguser);
if (conn->pgpass)
+ {
+ explicit_bzero(conn->pgpass, strlen(conn->pgpass));
free(conn->pgpass);
+ }
if (conn->pgpassfile)
free(conn->pgpassfile);
if (conn->keepalives)
@@ -6935,6 +6941,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
if (!ret)
{
/* Out of memory. XXX: an error message would be nice. */
+ explicit_bzero(buf, sizeof(buf));
return NULL;
}
@@ -6951,6 +6958,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
}
fclose(fp);
+ explicit_bzero(buf, sizeof(buf));
return NULL;
#undef LINELEN
base-commit: 1323bfce55109dd54ee164828aab7983d3020a25
--
2.22.0
On Sun, Jun 23, 2019 at 09:57:18PM +0200, Peter Eisentraut wrote:
On 2019-06-23 21:55, Peter Eisentraut wrote:
On 2019-06-21 15:25, Tom Lane wrote:
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
+#ifndef HAVE_EXPLICIT_BZERO +#define explicit_bzero(b, len) bzero(b, len) +#endifThis presumes that every platform has bzero, which is unsafe (POSIX
doesn't specify it) and is an assumption we kicked to the curb a dozen
years ago (067a5cdb3). Please use memset() for the substitute instead.
+1.
OK, done.
and with patch attached
CreateRole() and AlterRole() can manipulate a password in plain format
in memory. The cleanup could be done just after calling
encrypt_password() in user.c.
Could it be possible to add the new flag in pg_config.h.win32?
--
Michael
On Sun, Jun 23, 2019 at 09:56:40PM +0200, Peter Eisentraut wrote:
On 2019-06-21 15:45, Dagfinn Ilmari Mannsåker wrote:
Also, on Linux it requires libbsd: https://libbsd.freedesktop.org/
No, it's in glibc.
From man:
explicit_bzero() first appeared in glibc 2.25.
--
Michael
Michael Paquier <michael@paquier.xyz> writes:
On Sun, Jun 23, 2019 at 09:56:40PM +0200, Peter Eisentraut wrote:
On 2019-06-21 15:45, Dagfinn Ilmari Mannsåker wrote:
Also, on Linux it requires libbsd: https://libbsd.freedesktop.org/
No, it's in glibc.
From man:
explicit_bzero() first appeared in glibc 2.25.
Ah, I was looking on my Debian Stretch (stable) box, which only has
glibc 2.24. Buster (testing, due out next week) has 2.28 which indeed
has it.
- ilmari
--
- Twitter seems more influential [than blogs] in the 'gets reported in
the mainstream press' sense at least. - Matt McLeod
- That'd be because the content of a tweet is easier to condense down
to a mainstream media article. - Calle Dybedahl
On Mon, Jun 24, 2019 at 7:57 AM Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:
On 2019-06-23 21:55, Peter Eisentraut wrote:
On 2019-06-21 15:25, Tom Lane wrote:
years ago (067a5cdb3). Please use memset() for the substitute instead.
OK, done.
+#ifndef HAVE_EXPLICIT_BZERO
+#define explicit_bzero(b, len) memset(b, 0, len)
+#endif
I noticed some other libraries use memset through a function pointer
or at least define a function the compiler can't see.
and with patch attached
The ssl tests fail:
FATAL: could not load private key file "server-password.key": bad decrypt
That's apparently due to the passphrase being clobbered in the output
buffer before we've managed to use it:
@@ -118,6 +118,7 @@ run_ssl_passphrase_command(const char *prompt,
bool is_server_start, char *buf,
buf[--len] = '\0';
error:
+ explicit_bzero(buf, size);
pfree(command.data);
return len;
}
--
Thomas Munro
https://enterprisedb.com
On 2019-07-05 14:06, Thomas Munro wrote:
+#ifndef HAVE_EXPLICIT_BZERO +#define explicit_bzero(b, len) memset(b, 0, len) +#endifI noticed some other libraries use memset through a function pointer
or at least define a function the compiler can't see.
I don't understand what you are getting at here.
The ssl tests fail:
FATAL: could not load private key file "server-password.key": bad decrypt
That's apparently due to the passphrase being clobbered in the output
buffer before we've managed to use it:@@ -118,6 +118,7 @@ run_ssl_passphrase_command(const char *prompt,
bool is_server_start, char *buf,
buf[--len] = '\0';error:
+ explicit_bzero(buf, size);
pfree(command.data);
return len;
}
Yeah, that's a silly mistake. New patch attached.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
v3-0001-Use-explicit_bzero.patchtext/plain; charset=UTF-8; name=v3-0001-Use-explicit_bzero.patch; x-mac-creator=0; x-mac-type=0Download
From 2188d8455f207d378e46390ac4db59c201574229 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Fri, 5 Jul 2019 15:02:11 +0200
Subject: [PATCH v3] Use explicit_bzero
Discussion: https://www.postgresql.org/message-id/flat/42d26bde-5d5b-c90d-87ae-6cab875f73be%402ndquadrant.com
---
configure | 2 +-
configure.in | 1 +
src/backend/libpq/be-secure-common.c | 3 +++
src/include/pg_config.h.in | 3 +++
src/include/port.h | 4 ++++
src/interfaces/libpq/fe-connect.c | 8 ++++++++
6 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/configure b/configure
index 7a6bfc2339..dcdd362c67 100755
--- a/configure
+++ b/configure
@@ -15143,7 +15143,7 @@ fi
LIBS_including_readline="$LIBS"
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
-for ac_func in cbrt clock_gettime copyfile fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l
+for ac_func in cbrt clock_gettime copyfile explicit_bzero fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
diff --git a/configure.in b/configure.in
index dde3eec89f..57c82b0b56 100644
--- a/configure.in
+++ b/configure.in
@@ -1591,6 +1591,7 @@ AC_CHECK_FUNCS(m4_normalize([
cbrt
clock_gettime
copyfile
+ explicit_bzero
fdatasync
getifaddrs
getpeerucred
diff --git a/src/backend/libpq/be-secure-common.c b/src/backend/libpq/be-secure-common.c
index 877226d377..f2deba4243 100644
--- a/src/backend/libpq/be-secure-common.c
+++ b/src/backend/libpq/be-secure-common.c
@@ -86,6 +86,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
{
if (ferror(fh))
{
+ explicit_bzero(buf, size);
ereport(loglevel,
(errcode_for_file_access(),
errmsg("could not read from command \"%s\": %m",
@@ -97,6 +98,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
pclose_rc = ClosePipeStream(fh);
if (pclose_rc == -1)
{
+ explicit_bzero(buf, size);
ereport(loglevel,
(errcode_for_file_access(),
errmsg("could not close pipe to external command: %m")));
@@ -104,6 +106,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
}
else if (pclose_rc != 0)
{
+ explicit_bzero(buf, size);
ereport(loglevel,
(errcode_for_file_access(),
errmsg("command \"%s\" failed",
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 512213aa32..524873ba44 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -201,6 +201,9 @@
/* Define to 1 if you have the <editline/readline.h> header file. */
#undef HAVE_EDITLINE_READLINE_H
+/* Define to 1 if you have the `explicit_bzero' function. */
+#undef HAVE_EXPLICIT_BZERO
+
/* Define to 1 if you have the `fdatasync' function. */
#undef HAVE_FDATASYNC
diff --git a/src/include/port.h b/src/include/port.h
index b5c03d912b..7c8b5138ba 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -381,6 +381,10 @@ extern int isinf(double x);
#endif /* __clang__ && !__cplusplus */
#endif /* !HAVE_ISINF */
+#ifndef HAVE_EXPLICIT_BZERO
+#define explicit_bzero(b, len) memset(b, 0, len)
+#endif
+
#ifndef HAVE_STRTOF
extern float strtof(const char *nptr, char **endptr);
#endif
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index d70cf1f948..1b26bf856b 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -3884,7 +3884,10 @@ freePGconn(PGconn *conn)
if (conn->connhost[i].port != NULL)
free(conn->connhost[i].port);
if (conn->connhost[i].password != NULL)
+ {
+ explicit_bzero(conn->connhost[i].password, strlen(conn->connhost[i].password));
free(conn->connhost[i].password);
+ }
}
free(conn->connhost);
}
@@ -3918,7 +3921,10 @@ freePGconn(PGconn *conn)
if (conn->pguser)
free(conn->pguser);
if (conn->pgpass)
+ {
+ explicit_bzero(conn->pgpass, strlen(conn->pgpass));
free(conn->pgpass);
+ }
if (conn->pgpassfile)
free(conn->pgpassfile);
if (conn->keepalives)
@@ -6935,6 +6941,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
if (!ret)
{
/* Out of memory. XXX: an error message would be nice. */
+ explicit_bzero(buf, sizeof(buf));
return NULL;
}
@@ -6951,6 +6958,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
}
fclose(fp);
+ explicit_bzero(buf, sizeof(buf));
return NULL;
#undef LINELEN
base-commit: 594df378ffb04a72b713a13cc0a7166b3bced7b7
--
2.22.0
On Sat, Jul 6, 2019 at 1:07 AM Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:
On 2019-07-05 14:06, Thomas Munro wrote:
+#ifndef HAVE_EXPLICIT_BZERO +#define explicit_bzero(b, len) memset(b, 0, len) +#endifI noticed some other libraries use memset through a function pointer
or at least define a function the compiler can't see.I don't understand what you are getting at here.
Do we want to provide a replacement implementation that actually
prevents the compiler from generating no code in some circumstances?
Then I think we need at least a function defined in another
translation unit so the compiler can't see what it does, no?
--
Thomas Munro
https://enterprisedb.com
On 2019-07-05 23:02, Thomas Munro wrote:
On Sat, Jul 6, 2019 at 1:07 AM Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:On 2019-07-05 14:06, Thomas Munro wrote:
+#ifndef HAVE_EXPLICIT_BZERO +#define explicit_bzero(b, len) memset(b, 0, len) +#endifI noticed some other libraries use memset through a function pointer
or at least define a function the compiler can't see.I don't understand what you are getting at here.
Do we want to provide a replacement implementation that actually
prevents the compiler from generating no code in some circumstances?
Then I think we need at least a function defined in another
translation unit so the compiler can't see what it does, no?
I see. My premise, which should perhaps be explained in a comment at
least, is that on an operating system that does not provide
explicit_bzero() (or an obvious alternative), we don't care about
addressing this particular security concern, since the rest of the
operating system won't be secure in this way either. It shouldn't be
our job to fight this battle if the rest of the OS doesn't care.
An alternative patch would define explicit_bzero() to nothing if not
available. But that might create bugs if subsequent code relies on the
memory being zeroed, independent of security concerns, so I changed it
to use memset() so that at least logically both code paths are the same.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Sun, Jul 7, 2019 at 1:11 AM Peter Eisentraut
<peter.eisentraut@2ndquadrant.com> wrote:
I see. My premise, which should perhaps be explained in a comment at
least, is that on an operating system that does not provide
explicit_bzero() (or an obvious alternative), we don't care about
addressing this particular security concern, since the rest of the
operating system won't be secure in this way either. It shouldn't be
our job to fight this battle if the rest of the OS doesn't care.An alternative patch would define explicit_bzero() to nothing if not
available. But that might create bugs if subsequent code relies on the
memory being zeroed, independent of security concerns, so I changed it
to use memset() so that at least logically both code paths are the same.
Following a trail of crumbs beginning at OpenSSH's fallback
implementation of this[1]https://github.com/openssh/openssh-portable/blob/master/openbsd-compat/explicit_bzero.c, I learned that C11 has standardised
memset_s[2]http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#%5B%7B%22num%22%3A1353%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C0%2C792%2C0%5D for this purpose. Macs have memset_s but no
explicit_bzero. FreeBSD has both. I wonder if it'd be better to make
memset_s the function we use in our code, considering its standard
blessing and therefore likelihood of being available on every system
eventually.
Oh, I see the problem: glibc 2.25 introduced explicit_bzero, but no
version of glibc has memset_s yet. So that's why you did it that
way... RHEL 8 and Debian 10 ship with explicit_bzero. Bleugh.
[1]: https://github.com/openssh/openssh-portable/blob/master/openbsd-compat/explicit_bzero.c
[2]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#%5B%7B%22num%22%3A1353%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C0%2C792%2C0%5D
--
Thomas Munro
https://enterprisedb.com
On Mon, Jun 24, 2019 at 02:08:50PM +0900, Michael Paquier wrote:
CreateRole() and AlterRole() can manipulate a password in plain format
in memory. The cleanup could be done just after calling
encrypt_password() in user.c.Could it be possible to add the new flag in pg_config.h.win32?
While remembering about it... Shouldn't the memset(0) now happening in
base64.c for the encoding and encoding routines when facing a failure
use explicit_zero()?
--
Michael
On 2019-Jul-11, Thomas Munro wrote:
Following a trail of crumbs beginning at OpenSSH's fallback
implementation of this[1], I learned that C11 has standardised
memset_s[2] for this purpose. Macs have memset_s but no
explicit_bzero. FreeBSD has both. I wonder if it'd be better to make
memset_s the function we use in our code, considering its standard
blessing and therefore likelihood of being available on every system
eventually.
Sounds like a future-proof way would be to implement memset_s in
src/port if absent from the OS (using explicit_bzero and other tricks),
and use that.
Here's a portable implementation (includes _WIN32 and NetBSD's
explicit_memset) under ISC license:
https://github.com/jedisct1/libsodium/blob/master/src/libsodium/sodium/utils.c#L112
(from https://www.cryptologie.net/article/419/zeroing-memory-compiler-optimizations-and-memset_s/ )
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
On 2019-Jul-11, Thomas Munro wrote:
Following a trail of crumbs beginning at OpenSSH's fallback
implementation of this[1], I learned that C11 has standardised
memset_s[2] for this purpose. Macs have memset_s but no
explicit_bzero. FreeBSD has both. I wonder if it'd be better to make
memset_s the function we use in our code, considering its standard
blessing and therefore likelihood of being available on every system
eventually.
Sounds like a future-proof way would be to implement memset_s in
src/port if absent from the OS (using explicit_bzero and other tricks),
and use that.
+1 for using the C11-standard name, even if that's not anywhere
in the real world yet.
regards, tom lane
On 2019-07-11 03:11, Michael Paquier wrote:
On Mon, Jun 24, 2019 at 02:08:50PM +0900, Michael Paquier wrote:
CreateRole() and AlterRole() can manipulate a password in plain format
in memory. The cleanup could be done just after calling
encrypt_password() in user.c.Could it be possible to add the new flag in pg_config.h.win32?
While remembering about it... Shouldn't the memset(0) now happening in
base64.c for the encoding and encoding routines when facing a failure
use explicit_zero()?
base64.c doesn't know what the data it is dealing with is used for.
That should be the responsibility of the caller, no?
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 2019-07-18 00:45, Tom Lane wrote:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
On 2019-Jul-11, Thomas Munro wrote:
Following a trail of crumbs beginning at OpenSSH's fallback
implementation of this[1], I learned that C11 has standardised
memset_s[2] for this purpose. Macs have memset_s but no
explicit_bzero. FreeBSD has both. I wonder if it'd be better to make
memset_s the function we use in our code, considering its standard
blessing and therefore likelihood of being available on every system
eventually.Sounds like a future-proof way would be to implement memset_s in
src/port if absent from the OS (using explicit_bzero and other tricks),
and use that.+1 for using the C11-standard name, even if that's not anywhere
in the real world yet.
ISTM that a problem is that you cannot implement a replacement
memset_s() as a wrapper around explicit_bzero(), unless you also want to
implement the bound checking stuff. (The "s"/safe in this family of
functions refers to the bound checking, not the cannot-be-optimized-away
property.) The other way around it is easier.
Also, the "s" family of functions appears to be a quagmire of
controversy and incompatibility, so it's perhaps better to stay away
from it for the time being.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
On 2019-07-18 00:45, Tom Lane wrote:
+1 for using the C11-standard name, even if that's not anywhere
in the real world yet.
ISTM that a problem is that you cannot implement a replacement
memset_s() as a wrapper around explicit_bzero(), unless you also want to
implement the bound checking stuff. (The "s"/safe in this family of
functions refers to the bound checking, not the cannot-be-optimized-away
property.) The other way around it is easier.
Oh, hm.
Also, the "s" family of functions appears to be a quagmire of
controversy and incompatibility, so it's perhaps better to stay away
from it for the time being.
Fair enough.
regards, tom lane
Another patch, with various fallback implementations.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
v4-0001-Use-explicit_bzero.patchtext/plain; charset=UTF-8; name=v4-0001-Use-explicit_bzero.patch; x-mac-creator=0; x-mac-type=0Download
From c040e7aa31a7a12f804c8d32d403861390dec8d5 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 29 Jul 2019 11:25:49 +0200
Subject: [PATCH v4] Use explicit_bzero
Use the explicit_bzero() function in places where it is important that
security information such as passwords is cleared from memory. There
might be other places where it could be useful; this is just an
initial collection.
For platforms that don't have explicit_bzero(), provide various
fallback implementations. (explicit_bzero() itself isn't standard,
but as Linux/glibc and OpenBSD have it, it's the most common spelling,
so it makes sense to make that the invocation point.)
Discussion: https://www.postgresql.org/message-id/flat/42d26bde-5d5b-c90d-87ae-6cab875f73be%402ndquadrant.com
---
configure | 15 +++++++-
configure.in | 2 +
src/backend/libpq/be-secure-common.c | 3 ++
src/include/pg_config.h.in | 6 +++
src/include/port.h | 4 ++
src/interfaces/libpq/fe-connect.c | 8 ++++
src/port/explicit_bzero.c | 55 ++++++++++++++++++++++++++++
7 files changed, 92 insertions(+), 1 deletion(-)
create mode 100644 src/port/explicit_bzero.c
diff --git a/configure b/configure
index 7a6bfc2339..d3b1108218 100755
--- a/configure
+++ b/configure
@@ -15143,7 +15143,7 @@ fi
LIBS_including_readline="$LIBS"
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
-for ac_func in cbrt clock_gettime copyfile fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l
+for ac_func in cbrt clock_gettime copyfile fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memset_s memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -15808,6 +15808,19 @@ esac
fi
+ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero"
+if test "x$ac_cv_func_explicit_bzero" = xyes; then :
+ $as_echo "#define HAVE_EXPLICIT_BZERO 1" >>confdefs.h
+
+else
+ case " $LIBOBJS " in
+ *" explicit_bzero.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS explicit_bzero.$ac_objext"
+ ;;
+esac
+
+fi
+
ac_fn_c_check_func "$LINENO" "fls" "ac_cv_func_fls"
if test "x$ac_cv_func_fls" = xyes; then :
$as_echo "#define HAVE_FLS 1" >>confdefs.h
diff --git a/configure.in b/configure.in
index dde3eec89f..c639a32a79 100644
--- a/configure.in
+++ b/configure.in
@@ -1596,6 +1596,7 @@ AC_CHECK_FUNCS(m4_normalize([
getpeerucred
getrlimit
mbstowcs_l
+ memset_s
memmove
poll
posix_fallocate
@@ -1694,6 +1695,7 @@ fi
AC_REPLACE_FUNCS(m4_normalize([
crypt
dlopen
+ explicit_bzero
fls
getopt
getrusage
diff --git a/src/backend/libpq/be-secure-common.c b/src/backend/libpq/be-secure-common.c
index 4abbef5bf1..880b758e70 100644
--- a/src/backend/libpq/be-secure-common.c
+++ b/src/backend/libpq/be-secure-common.c
@@ -86,6 +86,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
{
if (ferror(fh))
{
+ explicit_bzero(buf, size);
ereport(loglevel,
(errcode_for_file_access(),
errmsg("could not read from command \"%s\": %m",
@@ -97,6 +98,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
pclose_rc = ClosePipeStream(fh);
if (pclose_rc == -1)
{
+ explicit_bzero(buf, size);
ereport(loglevel,
(errcode_for_file_access(),
errmsg("could not close pipe to external command: %m")));
@@ -104,6 +106,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
}
else if (pclose_rc != 0)
{
+ explicit_bzero(buf, size);
ereport(loglevel,
(errcode_for_file_access(),
errmsg("command \"%s\" failed",
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 512213aa32..8282d11dad 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -201,6 +201,9 @@
/* Define to 1 if you have the <editline/readline.h> header file. */
#undef HAVE_EDITLINE_READLINE_H
+/* Define to 1 if you have the `explicit_bzero' function. */
+#undef HAVE_EXPLICIT_BZERO
+
/* Define to 1 if you have the `fdatasync' function. */
#undef HAVE_FDATASYNC
@@ -401,6 +404,9 @@
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
+/* Define to 1 if you have the `memset_s' function. */
+#undef HAVE_MEMSET_S
+
/* Define to 1 if the system has the type `MINIDUMP_TYPE'. */
#undef HAVE_MINIDUMP_TYPE
diff --git a/src/include/port.h b/src/include/port.h
index b5c03d912b..0a4801434e 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -381,6 +381,10 @@ extern int isinf(double x);
#endif /* __clang__ && !__cplusplus */
#endif /* !HAVE_ISINF */
+#ifndef HAVE_EXPLICIT_BZERO
+extern void explicit_bzero(void *buf, size_t len);
+#endif
+
#ifndef HAVE_STRTOF
extern float strtof(const char *nptr, char **endptr);
#endif
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index a329ebbf93..01ad3ae3e5 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -3884,7 +3884,10 @@ freePGconn(PGconn *conn)
if (conn->connhost[i].port != NULL)
free(conn->connhost[i].port);
if (conn->connhost[i].password != NULL)
+ {
+ explicit_bzero(conn->connhost[i].password, strlen(conn->connhost[i].password));
free(conn->connhost[i].password);
+ }
}
free(conn->connhost);
}
@@ -3918,7 +3921,10 @@ freePGconn(PGconn *conn)
if (conn->pguser)
free(conn->pguser);
if (conn->pgpass)
+ {
+ explicit_bzero(conn->pgpass, strlen(conn->pgpass));
free(conn->pgpass);
+ }
if (conn->pgpassfile)
free(conn->pgpassfile);
if (conn->keepalives)
@@ -6934,6 +6940,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
if (!ret)
{
/* Out of memory. XXX: an error message would be nice. */
+ explicit_bzero(buf, sizeof(buf));
return NULL;
}
@@ -6950,6 +6957,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
}
fclose(fp);
+ explicit_bzero(buf, sizeof(buf));
return NULL;
#undef LINELEN
diff --git a/src/port/explicit_bzero.c b/src/port/explicit_bzero.c
new file mode 100644
index 0000000000..21164ded84
--- /dev/null
+++ b/src/port/explicit_bzero.c
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * explicit_bzero.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "c.h"
+
+#if defined(HAVE_MEMSET_S)
+
+void
+explicit_bzero(void *buf, size_t len)
+{
+ (void) memset_s(buf, len, 0, len);
+}
+
+#elif defined(WIN32)
+
+#include "c.h"
+
+void
+explicit_bzero(void *buf, size_t len)
+{
+ (void) SecureZeroMemory(buf, len);
+}
+
+#else
+
+#include "c.h"
+
+/*
+ * Indirect call through a volatile pointer to hopefully avoid dead-store
+ * optimisation eliminating the call. (Idea taken from OpenSSH.) We can't
+ * assume bzero() is present either, so for simplicity we define our own.
+ */
+
+static void
+bzero2(void *buf, size_t len)
+{
+ memset(buf, 0, len);
+}
+
+static void (* volatile bzero_p)(void *, size_t) = bzero2;
+
+void
+explicit_bzero(void *buf, size_t len)
+{
+ bzero_p(buf, len);
+}
+
+#endif
base-commit: 1e2fddfa33d3c7cc93ca3ee0f32852699bd3e012
--
2.22.0
On Mon, Jul 29, 2019 at 11:30:53AM +0200, Peter Eisentraut wrote:
Another patch, with various fallback implementations.
I have spotted some issues with this patch:
1) The list of port files @pgportfiles in Mkvcbuild.pm has not been
updated with the new file explicit_bzero.c, so the compilation would
fail with MSVC.
2) pg_config.h.win32 does not include the two new flags (same as
/messages/by-id/20190624050850.GE1637@paquier.xyz)
3) What about CreateRole() and AlterRole() which can manipulate a
password in plain format before hashing? (same message as previous
point).
Nit: src/port/explicit_bzero.c misses its IDENTIFICATION tag.
--
Michael
Hi,
On 2019-07-29 11:30:53 +0200, Peter Eisentraut wrote:
For platforms that don't have explicit_bzero(), provide various
fallback implementations. (explicit_bzero() itself isn't standard,
but as Linux/glibc and OpenBSD have it, it's the most common spelling,
so it makes sense to make that the invocation point.)
I think it's better to have a pg_explicit_bzero or such, and implement
that via the various platform dependant mechanisms. It's considerably
harder to understand code when one is surprised that a function normally
not available is called, the buildsystem part is really hard to
understand (with runtime and code filenames differing etc), and invites
API breakages. And it's not really more work to have our own name.
+/*------------------------------------------------------------------------- + * + * explicit_bzero.c + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + *------------------------------------------------------------------------- + */ + +#include "c.h" + +#if defined(HAVE_MEMSET_S) + +void +explicit_bzero(void *buf, size_t len) +{ + (void) memset_s(buf, len, 0, len); +} + +#elif defined(WIN32) + +#include "c.h"
Hm?
+/* + * Indirect call through a volatile pointer to hopefully avoid dead-store + * optimisation eliminating the call. (Idea taken from OpenSSH.) We can't + * assume bzero() is present either, so for simplicity we define our own. + */ + +static void +bzero2(void *buf, size_t len) +{ + memset(buf, 0, len); +} + +static void (* volatile bzero_p)(void *, size_t) = bzero2;
Hm, I'm not really sure that this does that much. Especially when the
call is via a function in the same translation unit.
+void +explicit_bzero(void *buf, size_t len) +{ + bzero_p(buf, len);
I've not followed this discussion. But why isn't the obvious
implementation here memset(...); pg_compiler_barrier()?
A quick web search indicates that that's what a bunch of projects in the
cryptography space also ended up with (well, __asm__ __volatile__("" :::
"memory"), which is what pg_compiler_barrier boils down to for
gcc/clang/compatibles).
Greetings,
Andres Freund
On Tue, Jul 30, 2019 at 5:58 PM Andres Freund <andres@anarazel.de> wrote:
+#include "c.h"
Hm?
Heh.
+static void (* volatile bzero_p)(void *, size_t) = bzero2;
Hm, I'm not really sure that this does that much. Especially when the
call is via a function in the same translation unit.
Yeah, I wondered the same (when reading the OpenSSH version). You'd
think you'd need a non-static global so it has to assume that it could
change.
+void +explicit_bzero(void *buf, size_t len) +{ + bzero_p(buf, len);I've not followed this discussion. But why isn't the obvious
implementation here memset(...); pg_compiler_barrier()?A quick web search indicates that that's what a bunch of projects in the
cryptography space also ended up with (well, __asm__ __volatile__("" :::
"memory"), which is what pg_compiler_barrier boils down to for
gcc/clang/compatibles).
At a glance, I think 3.4.3 of this 2017 paper says that might not work
on Clang and those other people might have a bug:
https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-yang.pdf
cfbot says:
fe-connect.obj : error LNK2019: unresolved external symbol
explicit_bzero referenced in function freePGconn
[C:\projects\postgresql\libpq.vcxproj]
Moved to next CF.
--
Thomas Munro
https://enterprisedb.com
Hi,
On 2019-08-01 20:08:15 +1200, Thomas Munro wrote:
On Tue, Jul 30, 2019 at 5:58 PM Andres Freund <andres@anarazel.de> wrote:
+#include "c.h" +static void (* volatile bzero_p)(void *, size_t) = bzero2;Hm, I'm not really sure that this does that much. Especially when the
call is via a function in the same translation unit.Yeah, I wondered the same (when reading the OpenSSH version). You'd
think you'd need a non-static global so it has to assume that it could
change.
The implementations in other projects I saw did the above trick, but
also marked the symbol as weak. Telling the compiler it can't know what
version will be used at runtime. But that adds a bunch of compiler
dependencies too.
+void +explicit_bzero(void *buf, size_t len) +{ + bzero_p(buf, len);I've not followed this discussion. But why isn't the obvious
implementation here memset(...); pg_compiler_barrier()?A quick web search indicates that that's what a bunch of projects in the
cryptography space also ended up with (well, __asm__ __volatile__("" :::
"memory"), which is what pg_compiler_barrier boils down to for
gcc/clang/compatibles).At a glance, I think 3.4.3 of this 2017 paper says that might not work
on Clang and those other people might have a bug:https://www.usenix.org/system/files/conference/usenixsecurity17/sec17-yang.pdf
https://bugs.llvm.org/show_bug.cgi?id=15495
We could just combine it with volatile out of paranoia anyway. But I'm
also more than bit doubtful about this bugreport. There's simply no
memory here. It's not that the memset is optimized away, it's that
there's no memory at all. It results in:
.file "test.c"
.globl foo # -- Begin function foo
.p2align 4, 0x90
.type foo,@function
foo: # @foo
.cfi_startproc
# %bb.0:
#APP
#NO_APP
retq
there's no secrets left over here. If you actuall force the memory be
filled, even if it's afterwards dead, you do get the memory
cleaned. E.g.
#include <string.h>
static void mybzero(char *buf, int len) {
memset(buf,0,len);
asm("" : : : "memory");
}
extern void grab_password(char *buf, int len);
int main(int argc, char **argv)
{
char buf[512];
grab_password(buf, sizeof(buf));
mybzero(buf, sizeof(buf));
return 0;
}
results in
main: # @main
.cfi_startproc
# %bb.0:
pushq %rbx
.cfi_def_cfa_offset 16
subq $512, %rsp # imm = 0x200
.cfi_def_cfa_offset 528
.cfi_offset %rbx, -16
movq %rsp, %rbx
movq %rbx, %rdi
movl $512, %esi # imm = 0x200
callq grab_password
movl $512, %edx # imm = 0x200
movq %rbx, %rdi
xorl %esi, %esi
callq memset
#APP
#NO_APP
xorl %eax, %eax
addq $512, %rsp # imm = 0x200
.cfi_def_cfa_offset 16
popq %rbx
.cfi_def_cfa_offset 8
retq
.Lfunc_end0:
.size main, .Lfunc_end0-main
.cfi_endproc
# -- End function
Although - and that is not surprising - if you lie and mark
grab_password as being pure (__attribute__((pure)), which signals the
function has no sideeffects except its return value), it'll optimize the
whole memory away again. But no secrets leaked again:
main: # @main
.cfi_startproc
# %bb.0:
#APP
#NO_APP
xorl %eax, %eax
retq
.Lfunc_end0:
.size main, .Lfunc_end0-main
.cfi_endproc
Out of paranoia we could go add add the additional step and have a
barrier variant that's variable specific, and make the __asm__
__volatile__ als take the input as "r"(buf), which'd prevent even this
issue (because now the memory is actually understood as being used).
Which turns out to be e.g. what google did for boringssl...
https://boringssl.googlesource.com/boringssl/+/ad1907fe73334d6c696c8539646c21b11178f20f%5E!/#F0
Greetings,
Andres Freund
On 2019-07-30 07:08, Michael Paquier wrote:
On Mon, Jul 29, 2019 at 11:30:53AM +0200, Peter Eisentraut wrote:
Another patch, with various fallback implementations.
I have spotted some issues with this patch:
1) The list of port files @pgportfiles in Mkvcbuild.pm has not been
updated with the new file explicit_bzero.c, so the compilation would
fail with MSVC.
2) pg_config.h.win32 does not include the two new flags (same as
/messages/by-id/20190624050850.GE1637@paquier.xyz)
Another patch, to attempt to fix the Windows build.
3) What about CreateRole() and AlterRole() which can manipulate a
password in plain format before hashing? (same message as previous
point).
If you want to secure CREATE ROLE foo PASSWORD 'plaintext' then you need
to also analyze memory usage in protocol processing and parsing and the
like. This would be a laborious and difficult to verify undertaking.
It's better to say, if you want to be secure, don't do that.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
v5-0001-Use-explicit_bzero.patchtext/plain; charset=UTF-8; name=v5-0001-Use-explicit_bzero.patch; x-mac-creator=0; x-mac-type=0Download
From df7a712cabe4f29b9bcc135db403e7ea8ed9f1e0 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 13 Aug 2019 10:25:17 +0200
Subject: [PATCH v5] Use explicit_bzero
Use the explicit_bzero() function in places where it is important that
security information such as passwords is cleared from memory. There
might be other places where it could be useful; this is just an
initial collection.
For platforms that don't have explicit_bzero(), provide various
fallback implementations. (explicit_bzero() itself isn't standard,
but as Linux/glibc and OpenBSD have it, it's the most common spelling,
so it makes sense to make that the invocation point.)
Discussion: https://www.postgresql.org/message-id/flat/42d26bde-5d5b-c90d-87ae-6cab875f73be%402ndquadrant.com
---
configure | 15 +++++++-
configure.in | 2 +
src/backend/libpq/be-secure-common.c | 3 ++
src/include/pg_config.h.in | 6 +++
src/include/pg_config.h.win32 | 6 +++
src/include/port.h | 4 ++
src/interfaces/libpq/fe-connect.c | 8 ++++
src/port/explicit_bzero.c | 55 ++++++++++++++++++++++++++++
src/tools/msvc/Mkvcbuild.pm | 2 +-
9 files changed, 99 insertions(+), 2 deletions(-)
create mode 100644 src/port/explicit_bzero.c
diff --git a/configure b/configure
index 7a6bfc2339..d3b1108218 100755
--- a/configure
+++ b/configure
@@ -15143,7 +15143,7 @@ fi
LIBS_including_readline="$LIBS"
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
-for ac_func in cbrt clock_gettime copyfile fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l
+for ac_func in cbrt clock_gettime copyfile fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memset_s memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -15808,6 +15808,19 @@ esac
fi
+ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero"
+if test "x$ac_cv_func_explicit_bzero" = xyes; then :
+ $as_echo "#define HAVE_EXPLICIT_BZERO 1" >>confdefs.h
+
+else
+ case " $LIBOBJS " in
+ *" explicit_bzero.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS explicit_bzero.$ac_objext"
+ ;;
+esac
+
+fi
+
ac_fn_c_check_func "$LINENO" "fls" "ac_cv_func_fls"
if test "x$ac_cv_func_fls" = xyes; then :
$as_echo "#define HAVE_FLS 1" >>confdefs.h
diff --git a/configure.in b/configure.in
index dde3eec89f..c639a32a79 100644
--- a/configure.in
+++ b/configure.in
@@ -1596,6 +1596,7 @@ AC_CHECK_FUNCS(m4_normalize([
getpeerucred
getrlimit
mbstowcs_l
+ memset_s
memmove
poll
posix_fallocate
@@ -1694,6 +1695,7 @@ fi
AC_REPLACE_FUNCS(m4_normalize([
crypt
dlopen
+ explicit_bzero
fls
getopt
getrusage
diff --git a/src/backend/libpq/be-secure-common.c b/src/backend/libpq/be-secure-common.c
index e8f27bc782..d801929ea2 100644
--- a/src/backend/libpq/be-secure-common.c
+++ b/src/backend/libpq/be-secure-common.c
@@ -87,6 +87,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
{
if (ferror(fh))
{
+ explicit_bzero(buf, size);
ereport(loglevel,
(errcode_for_file_access(),
errmsg("could not read from command \"%s\": %m",
@@ -98,6 +99,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
pclose_rc = ClosePipeStream(fh);
if (pclose_rc == -1)
{
+ explicit_bzero(buf, size);
ereport(loglevel,
(errcode_for_file_access(),
errmsg("could not close pipe to external command: %m")));
@@ -105,6 +107,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
}
else if (pclose_rc != 0)
{
+ explicit_bzero(buf, size);
ereport(loglevel,
(errcode_for_file_access(),
errmsg("command \"%s\" failed",
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 512213aa32..8282d11dad 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -201,6 +201,9 @@
/* Define to 1 if you have the <editline/readline.h> header file. */
#undef HAVE_EDITLINE_READLINE_H
+/* Define to 1 if you have the `explicit_bzero' function. */
+#undef HAVE_EXPLICIT_BZERO
+
/* Define to 1 if you have the `fdatasync' function. */
#undef HAVE_FDATASYNC
@@ -401,6 +404,9 @@
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
+/* Define to 1 if you have the `memset_s' function. */
+#undef HAVE_MEMSET_S
+
/* Define to 1 if the system has the type `MINIDUMP_TYPE'. */
#undef HAVE_MINIDUMP_TYPE
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 2d903c82b8..dcd0f7b31b 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -159,6 +159,9 @@
/* Define to 1 if you have the <editline/readline.h> header file. */
/* #undef HAVE_EDITLINE_READLINE_H */
+/* Define to 1 if you have the `explicit_bzero' function. */
+/* #undef HAVE_EXPLICIT_BZERO */
+
/* Define to 1 if you have the `fdatasync' function. */
/* #undef HAVE_FDATASYNC */
@@ -289,6 +292,9 @@
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
+/* Define to 1 if you have the `memset_s' function. */
+/* #undef HAVE_MEMSET_S */
+
/* Define to 1 if the system has the type `MINIDUMP_TYPE'. */
#define HAVE_MINIDUMP_TYPE 1
diff --git a/src/include/port.h b/src/include/port.h
index b5c03d912b..0a4801434e 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -381,6 +381,10 @@ extern int isinf(double x);
#endif /* __clang__ && !__cplusplus */
#endif /* !HAVE_ISINF */
+#ifndef HAVE_EXPLICIT_BZERO
+extern void explicit_bzero(void *buf, size_t len);
+#endif
+
#ifndef HAVE_STRTOF
extern float strtof(const char *nptr, char **endptr);
#endif
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index fa5af18ffa..33d923895d 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -3885,7 +3885,10 @@ freePGconn(PGconn *conn)
if (conn->connhost[i].port != NULL)
free(conn->connhost[i].port);
if (conn->connhost[i].password != NULL)
+ {
+ explicit_bzero(conn->connhost[i].password, strlen(conn->connhost[i].password));
free(conn->connhost[i].password);
+ }
}
free(conn->connhost);
}
@@ -3919,7 +3922,10 @@ freePGconn(PGconn *conn)
if (conn->pguser)
free(conn->pguser);
if (conn->pgpass)
+ {
+ explicit_bzero(conn->pgpass, strlen(conn->pgpass));
free(conn->pgpass);
+ }
if (conn->pgpassfile)
free(conn->pgpassfile);
if (conn->keepalives)
@@ -6931,6 +6937,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
if (!ret)
{
/* Out of memory. XXX: an error message would be nice. */
+ explicit_bzero(buf, sizeof(buf));
return NULL;
}
@@ -6947,6 +6954,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
}
fclose(fp);
+ explicit_bzero(buf, sizeof(buf));
return NULL;
#undef LINELEN
diff --git a/src/port/explicit_bzero.c b/src/port/explicit_bzero.c
new file mode 100644
index 0000000000..7e7f24ef97
--- /dev/null
+++ b/src/port/explicit_bzero.c
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * explicit_bzero.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/port/explicit_bzero.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "c.h"
+
+#if defined(HAVE_MEMSET_S)
+
+void
+explicit_bzero(void *buf, size_t len)
+{
+ (void) memset_s(buf, len, 0, len);
+}
+
+#elif defined(WIN32)
+
+void
+explicit_bzero(void *buf, size_t len)
+{
+ (void) SecureZeroMemory(buf, len);
+}
+
+#else
+
+/*
+ * Indirect call through a volatile pointer to hopefully avoid dead-store
+ * optimisation eliminating the call. (Idea taken from OpenSSH.) We can't
+ * assume bzero() is present either, so for simplicity we define our own.
+ */
+
+static void
+bzero2(void *buf, size_t len)
+{
+ memset(buf, 0, len);
+}
+
+static void (* volatile bzero_p)(void *, size_t) = bzero2;
+
+void
+explicit_bzero(void *buf, size_t len)
+{
+ bzero_p(buf, len);
+}
+
+#endif
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index d1d0aed07e..8e9f4f65bb 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -93,7 +93,7 @@ sub mkvcbuild
$solution = CreateSolution($vsVersion, $config);
our @pgportfiles = qw(
- chklocale.c crypt.c fls.c fseeko.c getrusage.c inet_aton.c random.c
+ chklocale.c crypt.c explicit_bzero.c fls.c fseeko.c getrusage.c inet_aton.c random.c
srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
dirent.c dlopen.c getopt.c getopt_long.c
base-commit: 66bde49d96a9ddacc49dcbdf1b47b5bd6e31ead5
--
2.22.0
On 2019-07-30 07:58, Andres Freund wrote:
I think it's better to have a pg_explicit_bzero or such, and implement
that via the various platform dependant mechanisms. It's considerably
harder to understand code when one is surprised that a function normally
not available is called, the buildsystem part is really hard to
understand (with runtime and code filenames differing etc), and invites
API breakages. And it's not really more work to have our own name.
explicit_bzero() is a pretty established and quasi-standard name by now,
not too different from other things in src/port/.
+/* + * Indirect call through a volatile pointer to hopefully avoid dead-store + * optimisation eliminating the call. (Idea taken from OpenSSH.) We can't + * assume bzero() is present either, so for simplicity we define our own. + */ + +static void +bzero2(void *buf, size_t len) +{ + memset(buf, 0, len); +} + +static void (* volatile bzero_p)(void *, size_t) = bzero2;Hm, I'm not really sure that this does that much. Especially when the
call is via a function in the same translation unit.
This is the fallback implementation from OpenSSH, so it's plausible that
it does something. It's worth verifying, of course.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Tue, Aug 13, 2019 at 10:30:39AM +0200, Peter Eisentraut wrote:
Another patch, to attempt to fix the Windows build.
I have not been able to test the compilation, but the changes look
good on this side.
--
Michael
On 2019-08-14 05:00, Michael Paquier wrote:
On Tue, Aug 13, 2019 at 10:30:39AM +0200, Peter Eisentraut wrote:
Another patch, to attempt to fix the Windows build.
I have not been able to test the compilation, but the changes look
good on this side.
Rebased patch, no functionality changes.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
v6-0001-Use-explicit_bzero.patchtext/plain; charset=UTF-8; name=v6-0001-Use-explicit_bzero.patch; x-mac-creator=0; x-mac-type=0Download
From 36c8521f8c75517670aa01d064e8b4c117765f87 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 13 Aug 2019 10:25:17 +0200
Subject: [PATCH v6] Use explicit_bzero
Use the explicit_bzero() function in places where it is important that
security information such as passwords is cleared from memory. There
might be other places where it could be useful; this is just an
initial collection.
For platforms that don't have explicit_bzero(), provide various
fallback implementations. (explicit_bzero() itself isn't standard,
but as Linux/glibc and OpenBSD have it, it's the most common spelling,
so it makes sense to make that the invocation point.)
Discussion: https://www.postgresql.org/message-id/flat/42d26bde-5d5b-c90d-87ae-6cab875f73be%402ndquadrant.com
---
configure | 15 +++++++-
configure.in | 2 +
src/backend/libpq/be-secure-common.c | 3 ++
src/include/pg_config.h.in | 6 +++
src/include/pg_config.h.win32 | 6 +++
src/include/port.h | 4 ++
src/interfaces/libpq/fe-connect.c | 8 ++++
src/port/explicit_bzero.c | 55 ++++++++++++++++++++++++++++
src/tools/msvc/Mkvcbuild.pm | 2 +-
9 files changed, 99 insertions(+), 2 deletions(-)
create mode 100644 src/port/explicit_bzero.c
diff --git a/configure b/configure
index f14709ed1e..b3c92764be 100755
--- a/configure
+++ b/configure
@@ -15087,7 +15087,7 @@ fi
LIBS_including_readline="$LIBS"
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
-for ac_func in cbrt clock_gettime copyfile fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l
+for ac_func in cbrt clock_gettime copyfile fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memset_s memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul strsignal symlink sync_file_range uselocale utime utimes wcstombs_l
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -15739,6 +15739,19 @@ esac
fi
+ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero"
+if test "x$ac_cv_func_explicit_bzero" = xyes; then :
+ $as_echo "#define HAVE_EXPLICIT_BZERO 1" >>confdefs.h
+
+else
+ case " $LIBOBJS " in
+ *" explicit_bzero.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS explicit_bzero.$ac_objext"
+ ;;
+esac
+
+fi
+
ac_fn_c_check_func "$LINENO" "fls" "ac_cv_func_fls"
if test "x$ac_cv_func_fls" = xyes; then :
$as_echo "#define HAVE_FLS 1" >>confdefs.h
diff --git a/configure.in b/configure.in
index 805cf8617b..0d16c1a971 100644
--- a/configure.in
+++ b/configure.in
@@ -1594,6 +1594,7 @@ AC_CHECK_FUNCS(m4_normalize([
getpeerucred
getrlimit
mbstowcs_l
+ memset_s
memmove
poll
posix_fallocate
@@ -1691,6 +1692,7 @@ fi
AC_REPLACE_FUNCS(m4_normalize([
dlopen
+ explicit_bzero
fls
getopt
getrusage
diff --git a/src/backend/libpq/be-secure-common.c b/src/backend/libpq/be-secure-common.c
index e8f27bc782..d801929ea2 100644
--- a/src/backend/libpq/be-secure-common.c
+++ b/src/backend/libpq/be-secure-common.c
@@ -87,6 +87,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
{
if (ferror(fh))
{
+ explicit_bzero(buf, size);
ereport(loglevel,
(errcode_for_file_access(),
errmsg("could not read from command \"%s\": %m",
@@ -98,6 +99,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
pclose_rc = ClosePipeStream(fh);
if (pclose_rc == -1)
{
+ explicit_bzero(buf, size);
ereport(loglevel,
(errcode_for_file_access(),
errmsg("could not close pipe to external command: %m")));
@@ -105,6 +107,7 @@ run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf,
}
else if (pclose_rc != 0)
{
+ explicit_bzero(buf, size);
ereport(loglevel,
(errcode_for_file_access(),
errmsg("command \"%s\" failed",
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index d876926c21..c6014e83fa 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -195,6 +195,9 @@
/* Define to 1 if you have the <editline/readline.h> header file. */
#undef HAVE_EDITLINE_READLINE_H
+/* Define to 1 if you have the `explicit_bzero' function. */
+#undef HAVE_EXPLICIT_BZERO
+
/* Define to 1 if you have the `fdatasync' function. */
#undef HAVE_FDATASYNC
@@ -395,6 +398,9 @@
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
+/* Define to 1 if you have the `memset_s' function. */
+#undef HAVE_MEMSET_S
+
/* Define to 1 if the system has the type `MINIDUMP_TYPE'. */
#undef HAVE_MINIDUMP_TYPE
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index fc50528590..5bbf476990 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -153,6 +153,9 @@
/* Define to 1 if you have the <editline/readline.h> header file. */
/* #undef HAVE_EDITLINE_READLINE_H */
+/* Define to 1 if you have the `explicit_bzero' function. */
+/* #undef HAVE_EXPLICIT_BZERO */
+
/* Define to 1 if you have the `fdatasync' function. */
/* #undef HAVE_FDATASYNC */
@@ -283,6 +286,9 @@
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
+/* Define to 1 if you have the `memset_s' function. */
+/* #undef HAVE_MEMSET_S */
+
/* Define to 1 if the system has the type `MINIDUMP_TYPE'. */
#define HAVE_MINIDUMP_TYPE 1
diff --git a/src/include/port.h b/src/include/port.h
index 55619d893c..30b6378ae5 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -378,6 +378,10 @@ extern int isinf(double x);
#endif /* __clang__ && !__cplusplus */
#endif /* !HAVE_ISINF */
+#ifndef HAVE_EXPLICIT_BZERO
+extern void explicit_bzero(void *buf, size_t len);
+#endif
+
#ifndef HAVE_STRTOF
extern float strtof(const char *nptr, char **endptr);
#endif
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 7f1fd2f45e..9a5aa1a3c5 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -3885,7 +3885,10 @@ freePGconn(PGconn *conn)
if (conn->connhost[i].port != NULL)
free(conn->connhost[i].port);
if (conn->connhost[i].password != NULL)
+ {
+ explicit_bzero(conn->connhost[i].password, strlen(conn->connhost[i].password));
free(conn->connhost[i].password);
+ }
}
free(conn->connhost);
}
@@ -3919,7 +3922,10 @@ freePGconn(PGconn *conn)
if (conn->pguser)
free(conn->pguser);
if (conn->pgpass)
+ {
+ explicit_bzero(conn->pgpass, strlen(conn->pgpass));
free(conn->pgpass);
+ }
if (conn->pgpassfile)
free(conn->pgpassfile);
if (conn->keepalives)
@@ -6931,6 +6937,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
if (!ret)
{
/* Out of memory. XXX: an error message would be nice. */
+ explicit_bzero(buf, sizeof(buf));
return NULL;
}
@@ -6947,6 +6954,7 @@ passwordFromFile(const char *hostname, const char *port, const char *dbname,
}
fclose(fp);
+ explicit_bzero(buf, sizeof(buf));
return NULL;
#undef LINELEN
diff --git a/src/port/explicit_bzero.c b/src/port/explicit_bzero.c
new file mode 100644
index 0000000000..7e7f24ef97
--- /dev/null
+++ b/src/port/explicit_bzero.c
@@ -0,0 +1,55 @@
+/*-------------------------------------------------------------------------
+ *
+ * explicit_bzero.c
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/port/explicit_bzero.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "c.h"
+
+#if defined(HAVE_MEMSET_S)
+
+void
+explicit_bzero(void *buf, size_t len)
+{
+ (void) memset_s(buf, len, 0, len);
+}
+
+#elif defined(WIN32)
+
+void
+explicit_bzero(void *buf, size_t len)
+{
+ (void) SecureZeroMemory(buf, len);
+}
+
+#else
+
+/*
+ * Indirect call through a volatile pointer to hopefully avoid dead-store
+ * optimisation eliminating the call. (Idea taken from OpenSSH.) We can't
+ * assume bzero() is present either, so for simplicity we define our own.
+ */
+
+static void
+bzero2(void *buf, size_t len)
+{
+ memset(buf, 0, len);
+}
+
+static void (* volatile bzero_p)(void *, size_t) = bzero2;
+
+void
+explicit_bzero(void *buf, size_t len)
+{
+ bzero_p(buf, len);
+}
+
+#endif
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 2eab635898..239f13cc12 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -93,7 +93,7 @@ sub mkvcbuild
$solution = CreateSolution($vsVersion, $config);
our @pgportfiles = qw(
- chklocale.c fls.c fseeko.c getrusage.c inet_aton.c random.c
+ chklocale.c explicit_bzero.c fls.c fseeko.c getrusage.c inet_aton.c random.c
srandom.c getaddrinfo.c gettimeofday.c inet_net_ntop.c kill.c open.c
erand48.c snprintf.c strlcat.c strlcpy.c dirmod.c noblock.c path.c
dirent.c dlopen.c getopt.c getopt_long.c
base-commit: 06fdc4e4d33c40dbf886565336574da5566878f4
--
2.22.0
On 2019-Aug-24, Peter Eisentraut wrote:
On 2019-08-14 05:00, Michael Paquier wrote:
On Tue, Aug 13, 2019 at 10:30:39AM +0200, Peter Eisentraut wrote:
Another patch, to attempt to fix the Windows build.
I have not been able to test the compilation, but the changes look
good on this side.Rebased patch, no functionality changes.
Marked RfC. Can we get on with this?
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Wed, Sep 04, 2019 at 04:38:21PM -0400, Alvaro Herrera wrote:
Marked RfC. Can we get on with this?
FWIW, I have been able to test this one on Windows with MSVC and
things are handled correctly.
--
Michael
On 2019-09-05 04:12, Michael Paquier wrote:
On Wed, Sep 04, 2019 at 04:38:21PM -0400, Alvaro Herrera wrote:
Marked RfC. Can we get on with this?
FWIW, I have been able to test this one on Windows with MSVC and
things are handled correctly.
committed
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Hi,
On 2019-09-05 08:38:36 +0200, Peter Eisentraut wrote:
On 2019-09-05 04:12, Michael Paquier wrote:
On Wed, Sep 04, 2019 at 04:38:21PM -0400, Alvaro Herrera wrote:
Marked RfC. Can we get on with this?
FWIW, I have been able to test this one on Windows with MSVC and
things are handled correctly.committed
I still think this change is done wrongly, by providing an
implementation for a library function implemented in various
projects. If you e.g. dynamically load a library that implements its own
version of bzero, ours will replace it in many cases.
I think all this implementation actually guarantees is that bzero2 is
read, but not that the copy is not elided. In practice that's *probably*
good enough, but a compiler could just check whether bzero_p points to
memset.
http://cseweb.ucsd.edu/~lerner/papers/dse-usenix2017.pdf
https://boringssl-review.googlesource.com/c/boringssl/+/1339/
I think we have absolutely no business possibly intercepting / replacing
actually securely coded implementations of sensitive functions.
Greetings,
Andres Freund
On 2019-09-09 17:18, Andres Freund wrote:
I think all this implementation actually guarantees is that bzero2 is
read, but not that the copy is not elided. In practice that's *probably*
good enough, but a compiler could just check whether bzero_p points to
memset.
Are you saying that the replacement implementation we provide is not
good enough? If so, I'm happy to look at alternatives. But that's the
design from OpenSSH, so if that is wrong, then there are bigger
problems. We could also take the OpenBSD implementation, but that has a
GCC-ish dependency, so we would probably want the OpenSSH implementation
as a fallback anyway.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services