Skip to content

Commit 9292546

Browse files
danielgustafssonCommitfest Bot
authored and
Commitfest Bot
committed
libpq: Add support for dumping SSL keylog to file
This adds a new connection parameter which instructs libpq to write out keymaterial clientside into a file in order to make connection debugging with Wireshark and similar tools possible. The file format used is the standardized NSS format. Author: Abhishek Chanda <[email protected]> Co-authored-by: Daniel Gustafsson <[email protected]> Reviewed-by: Jacob Champion <[email protected]> Discussion: https://postgr.es/m/CAKiP-K85C8uQbzXKWf5wHQPkuygGUGcufke713iHmYWOe9q2dA@mail.gmail.com
1 parent 6da2ba1 commit 9292546

File tree

9 files changed

+120
-2
lines changed

9 files changed

+120
-2
lines changed

‎configure

+1-1
Original file line numberDiff line numberDiff line change
@@ -12931,7 +12931,7 @@ fi
1293112931
done
1293212932

1293312933
# Function introduced in OpenSSL 1.1.1, not in LibreSSL.
12934-
for ac_func in X509_get_signature_info SSL_CTX_set_num_tickets
12934+
for ac_func in X509_get_signature_info SSL_CTX_set_num_tickets SSL_CTX_set_keylog_callback
1293512935
do :
1293612936
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
1293712937
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"

‎configure.ac

+1-1
Original file line numberDiff line numberDiff line change
@@ -1382,7 +1382,7 @@ if test "$with_ssl" = openssl ; then
13821382
# Function introduced in OpenSSL 1.0.2, not in LibreSSL.
13831383
AC_CHECK_FUNCS([SSL_CTX_set_cert_cb])
13841384
# Function introduced in OpenSSL 1.1.1, not in LibreSSL.
1385-
AC_CHECK_FUNCS([X509_get_signature_info SSL_CTX_set_num_tickets])
1385+
AC_CHECK_FUNCS([X509_get_signature_info SSL_CTX_set_num_tickets SSL_CTX_set_keylog_callback])
13861386
AC_DEFINE([USE_OPENSSL], 1, [Define to 1 to build with OpenSSL support. (--with-ssl=openssl)])
13871387
elif test "$with_ssl" != no ; then
13881388
AC_MSG_ERROR([--with-ssl must specify openssl])

‎doc/src/sgml/libpq.sgml

+24
Original file line numberDiff line numberDiff line change
@@ -1918,6 +1918,30 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
19181918
</listitem>
19191919
</varlistentry>
19201920

1921+
<varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
1922+
<term><literal>sslkeylogfile</literal></term>
1923+
<listitem>
1924+
<para>
1925+
This parameter specifies the location where <literal>libpq</literal>
1926+
will log keys used in this SSL context. This is useful for debugging
1927+
<productname>PostgreSQL</productname> protocol interactions or client
1928+
connections using network inspection tools like
1929+
<productname>Wireshark</productname>. This parameter is ignored if an
1930+
SSL connection is not made, or if <productname>LibreSSL</productname>
1931+
is used (<productname>LibreSSL</productname> does not support key
1932+
logging). Keys are logged using the <productname>NSS</productname>
1933+
format.
1934+
<warning>
1935+
<para>
1936+
Key logging will expose potentially sensitive information in the
1937+
keylog file. Keylog files should be handled with the same care as
1938+
<xref linkend="libpq-connect-sslkey" /> files.
1939+
</para>
1940+
</warning>
1941+
</para>
1942+
</listitem>
1943+
</varlistentry>
1944+
19211945
<varlistentry id="libpq-connect-sslpassword" xreflabel="sslpassword">
19221946
<term><literal>sslpassword</literal></term>
19231947
<listitem>

‎meson.build

+1
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,7 @@ if sslopt in ['auto', 'openssl']
14791479
# Function introduced in OpenSSL 1.1.1, not in LibreSSL.
14801480
['X509_get_signature_info'],
14811481
['SSL_CTX_set_num_tickets'],
1482+
['SSL_CTX_set_keylog_callback'],
14821483
]
14831484

14841485
are_openssl_funcs_complete = true

‎src/include/pg_config.h.in

+3
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,9 @@
368368
/* Define to 1 if you have the `SSL_CTX_set_ciphersuites' function. */
369369
#undef HAVE_SSL_CTX_SET_CIPHERSUITES
370370

371+
/* Define to 1 if you have the `SSL_CTX_set_keylog_callback' function. */
372+
#undef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
373+
371374
/* Define to 1 if you have the `SSL_CTX_set_num_tickets' function. */
372375
#undef HAVE_SSL_CTX_SET_NUM_TICKETS
373376

‎src/interfaces/libpq/fe-connect.c

+4
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
391391
"OAuth-Scope", "", 15,
392392
offsetof(struct pg_conn, oauth_scope)},
393393

394+
{"sslkeylogfile", NULL, NULL, NULL,
395+
"SSL-Key-Log-File", "", 0, /* sizeof("") = 0 */
396+
offsetof(struct pg_conn, sslkeylogfile)},
397+
394398
/* Terminating entry --- MUST BE LAST */
395399
{NULL, NULL, NULL, NULL,
396400
NULL, NULL, 0}

‎src/interfaces/libpq/fe-secure-openssl.c

+58
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
* include <wincrypt.h>, but some other Windows headers do.)
5858
*/
5959
#include "common/openssl.h"
60+
#include <openssl/ssl.h>
6061
#include <openssl/conf.h>
6162
#ifdef USE_SSL_ENGINE
6263
#include <openssl/engine.h>
@@ -684,6 +685,49 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
684685
/* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
685686
static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
686687

688+
#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
689+
/*
690+
* SSL Key Logging callback
691+
*
692+
* This callback lets the user store all key material to a file for debugging
693+
* purposes. The file will be written using the NSS keylog format. LibreSSL
694+
* 3.5 introduced stub function to set the callback for OpenSSL compatibility
695+
* but the callback is never invoked.
696+
*/
697+
static void
698+
SSL_CTX_keylog_cb(const SSL *ssl, const char *line)
699+
{
700+
int fd;
701+
mode_t old_umask;
702+
ssize_t rc;
703+
PGconn *conn = SSL_get_app_data(ssl);
704+
705+
if (conn == NULL)
706+
return;
707+
708+
old_umask = umask(077);
709+
fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
710+
umask(old_umask);
711+
712+
if (fd == -1)
713+
{
714+
libpq_append_conn_error(conn, "could not open ssl keylog file %s: %s",
715+
conn->sslkeylogfile, pg_strerror(errno));
716+
return;
717+
}
718+
719+
/* line is guaranteed by OpenSSL to be NUL terminated */
720+
rc = write(fd, line, strlen(line));
721+
if (rc < 0)
722+
libpq_append_conn_error(conn, "could not write to ssl keylog file %s: %s",
723+
conn->sslkeylogfile, pg_strerror(errno));
724+
else
725+
rc = write(fd, "\n", 1);
726+
(void) rc; /* silence compiler warnings */
727+
close(fd);
728+
}
729+
#endif
730+
687731
/*
688732
* Create per-connection SSL object, and load the client certificate,
689733
* private key, and trusted CA certs.
@@ -1000,6 +1044,20 @@ initialize_SSL(PGconn *conn)
10001044
}
10011045
conn->ssl_in_use = true;
10021046

1047+
if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
1048+
{
1049+
#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
1050+
SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
1051+
#else
1052+
#ifdef LIBRESSL_VERSION_NUMBER
1053+
fprintf(stderr, libpq_gettext("WARNING: sslkeylogfile support requires OpenSSL\n"));
1054+
#else
1055+
fprintf(stderr, libpq_gettext("WARNING: libpq was not built with sslkeylogfile support\n"));
1056+
#endif
1057+
#endif
1058+
}
1059+
1060+
10031061
/*
10041062
* SSL contexts are reference counted by OpenSSL. We can free it as soon
10051063
* as we have created the SSL object, and it will stick around for as long

‎src/interfaces/libpq/libpq-int.h

+1
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ struct pg_conn
424424
char *load_balance_hosts; /* load balance over hosts */
425425
char *scram_client_key; /* base64-encoded SCRAM client key */
426426
char *scram_server_key; /* base64-encoded SCRAM server key */
427+
char *sslkeylogfile; /* where should the client write ssl keylogs */
427428

428429
bool cancelRequest; /* true if this connection is used to send a
429430
* cancel request, instead of being a normal

‎src/test/ssl/t/001_ssltests.pl

+27
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,33 @@ sub switch_server_cert
147147
$common_connstr =
148148
"$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
149149

150+
SKIP:
151+
{
152+
skip "Keylogging is not supported with LibreSSL", 5 if $libressl;
153+
154+
my $tempdir = PostgreSQL::Test::Utils::tempdir;
155+
my @status;
156+
157+
# Properly escape backslashes in the path
158+
$tempdir =~ s/\\/\\\\/g;
159+
160+
# Connect should work with a given sslkeylogfile
161+
$node->connect_ok(
162+
"$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/key.txt sslmode=require",
163+
"connect with server root cert and sslkeylogfile=$tempdir/key.txt");
164+
165+
# Verify the key file exists
166+
ok(-f "$tempdir/key.txt", "keylog file exists at: $tempdir/key.txt");
167+
168+
# Skip permission checks on Windows/Cygwin
169+
skip "Permissions check not enforced on Windows", 2
170+
if ($windows_os || $Config::Config{osname} eq 'cygwin');
171+
172+
ok( (@status = stat("$tempdir/key.txt")),
173+
"keylog file exists and returned status");
174+
ok(@status && !($status[2] & 0006), "keylog file is not world readable");
175+
}
176+
150177
# The server should not accept non-SSL connections.
151178
$node->connect_fails(
152179
"$common_connstr sslmode=disable",

0 commit comments

Comments
 (0)