Adding support for SSLKEYLOGFILE in the frontend

Started by Abhishek Chandaabout 1 year ago45 messages
#1Abhishek Chanda
abhishek.becs@gmail.com
1 attachment(s)

Hello folks,

Attached is a patch to add support for logging secrets used in TLS
connection after psql is initialized. This adds a new env var
SSLKEYLOGFILE on the client side that points to a text file where keys
will be logged. If a user runs psql multiple times with the same
SSLKEYLOGFILE, new entries will be appended to that file. There is no
change in behavior if that env var is not set or set to an empty
string. This is useful for cases when a client wants to analyze TCP
packets using a tool like wireshark while using TLS. This will enable
wireshark to decrypt the packets and decode as postgres wire protocol
messages. I did not add this to the backend because I thought using
wireshark is more common on the frontend.

The keylogfile format is documented here
https://www.ietf.org/archive/id/draft-thomson-tls-keylogfile-00.html

Example usage:

root@guest:~/postgres# SSLKEYLOGFILE=./key.txt
/usr/local/pgsql/bin/psql
"postgresql://user:pass@host:5432/postgres?sslmode=require"
psql (18devel, server 17.2)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384,
compression: off, ALPN: postgresql)
Type "help" for help.

postgres=>
\q
root@guest:~/postgres# cat key.txt
SERVER_HANDSHAKE_TRAFFIC_SECRET ****
EXPORTER_SECRET ****
SERVER_TRAFFIC_SECRET_0 ****
CLIENT_HANDSHAKE_TRAFFIC_SECRET ***
CLIENT_TRAFFIC_SECRET_0 ****

A few things I am not sure about:
1. Where should I add automated tests and docs for this? I did not see
any unit tests for the surrounding functions.
2. Should I use perror to report error here? I did not want to use
libpq_append_conn_error because this is not a connection related
error.

Please let me know if I can clarify anything.

--
Thanks and regards
Abhishek Chanda

Attachments:

0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchapplication/octet-stream; name=0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchDownload
From 9c8b3f8e9ec52cf730b64eebb80f7b240584866d Mon Sep 17 00:00:00 2001
From: Abhishek Chanda <abhishek.becs@gmail.com>
Date: Tue, 7 Jan 2025 21:54:08 -0600
Subject: [PATCH] Add support for dumping SSL keylog to a file

---
 src/interfaces/libpq/fe-secure-openssl.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 5bb9d9779d..e0d4f5befd 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -86,6 +86,7 @@ static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
 static int	ssl_protocol_version_to_openssl(const char *protocol);
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line);
 
 /* ------------------------------------------------------------ */
 /*			 Procedures common to all secure sessions			*/
@@ -684,6 +685,22 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 /* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
 static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
 
+/* This is a callback that writes to a given SSLKEYLOGFILE file */
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line) {
+    FILE *log_file;
+    char *keylog_path = getenv("SSLKEYLOGFILE");
+    if (keylog_path == NULL || strlen(keylog_path) == 0)
+           return;
+
+    log_file = fopen(keylog_path, "a");
+    if (log_file != NULL) {
+        fprintf(log_file, "%s\n", line);
+        fclose(log_file);
+    } else {
+        perror("Unable to open key log file");
+    }
+}
+
 /*
  *	Create per-connection SSL object, and load the client certificate,
  *	private key, and trusted CA certs.
@@ -733,6 +750,7 @@ initialize_SSL(PGconn *conn)
 		return -1;
 	}
 
+    SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
 	/*
 	 * Delegate the client cert password prompt to the libpq wrapper callback
 	 * if any is defined.
-- 
2.47.1

#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Abhishek Chanda (#1)
Re: Adding support for SSLKEYLOGFILE in the frontend

Abhishek Chanda <abhishek.becs@gmail.com> writes:

Attached is a patch to add support for logging secrets used in TLS
connection after psql is initialized. This adds a new env var
SSLKEYLOGFILE on the client side that points to a text file where keys
will be logged.

I wonder if this poses a security risk, ie something forcing
extraction of TLS secrets at a distance via the environment variable.
I think it might be safer if we only accepted it as a connection
parameter and not via an environment variable. (I'm aware of the
rule of thumb that if you control the environment then you own your
callees anyway, via modifying PATH and suchlike. But there's a lot
of code that thinks it can sanitize the environment by overriding
PATH and other well-known variables. Nobody is going to be aware
that SSLKEYLOGFILE is a security hazard.)

2. Should I use perror to report error here?

No.

I did not want to use
libpq_append_conn_error because this is not a connection related
error.

Yes it is, IMO anyway. Anything like this should be treated as a
libpq connection parameter. The reason you couldn't find a place
to document this feature is you were ignoring libpq's conventions.

Just looking at the code itself, I'm a bit distressed that it's
not making any effort to secure the log file against prying eyes.
Shouldn't we be trying to create it with mode 0600?

regards, tom lane

#3Daniel Gustafsson
daniel@yesql.se
In reply to: Tom Lane (#2)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 9 Jan 2025, at 02:17, Tom Lane <tgl@sss.pgh.pa.us> wrote:

I think it might be safer if we only accepted it as a connection
parameter and not via an environment variable.

+1

--
Daniel Gustafsson

#4Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Tom Lane (#2)
Re: Adding support for SSLKEYLOGFILE in the frontend

On Wed, Jan 8, 2025 at 5:17 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

I think it might be safer if we only accepted it as a connection
parameter and not via an environment variable.

Making it a connection parameter also keeps us from colliding with any
other linked libraries' use of SSLKEYLOGFILE (I'm thinking about
libcurl at the moment, but I think maybe NSS used it too?).

--Jacob

#5Abhishek Chanda
abhishek.becs@gmail.com
In reply to: Jacob Champion (#4)
1 attachment(s)
Re: Adding support for SSLKEYLOGFILE in the frontend

Thanks for the feedback, everyone. Attached a followup with the
following changes compared to the initial version:

1. Converted sslkeylogfile to a connection parameter
2. Added a mechanism to chmod the key log file to 0600
3. Added docs and tests

I tested this manually. Also ran make check and make check-world
locally. Please let me know if this needs any other changes.

Thanks

On Thu, Jan 9, 2025 at 2:36 PM Jacob Champion
<jacob.champion@enterprisedb.com> wrote:

On Wed, Jan 8, 2025 at 5:17 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:

I think it might be safer if we only accepted it as a connection
parameter and not via an environment variable.

Making it a connection parameter also keeps us from colliding with any
other linked libraries' use of SSLKEYLOGFILE (I'm thinking about
libcurl at the moment, but I think maybe NSS used it too?).

--Jacob

--
Thanks and regards
Abhishek Chanda

Attachments:

v2-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchapplication/octet-stream; name=v2-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchDownload
From 082b9ddb57454776ffe30d6c11a53650ca859a7a Mon Sep 17 00:00:00 2001
From: Abhishek Chanda <abhishek.becs@gmail.com>
Date: Tue, 7 Jan 2025 21:54:08 -0600
Subject: [PATCH v2] Add support for dumping SSL keylog to a file

This patch adds a new connection parameter which is used by libpq
to write keys used in a SSL context
---
 doc/src/sgml/libpq.sgml                  | 22 ++++++++++++++++++++
 src/interfaces/libpq/fe-connect.c        |  5 +++++
 src/interfaces/libpq/fe-secure-openssl.c | 26 ++++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h         |  1 +
 src/test/perl/PostgreSQL/Test/Utils.pm   |  1 +
 src/test/regress/pg_regress.c            |  1 +
 6 files changed, 56 insertions(+)

diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 105b22b317..e2d36efe21 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1909,6 +1909,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
+      <term><literal>sslkeylogfile</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the location where libpq will log keys
+        used in this SSL context. This is useful for debugging postgres
+        protocol using tools like wireshark. This parameter is ignored if an
+        SSL connection is not made.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslpassword" xreflabel="sslpassword">
       <term><literal>sslpassword</literal></term>
       <listitem>
@@ -9032,6 +9044,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGSSLKEYLOGFILE</envar></primary>
+      </indexterm>
+      <envar>PGSSLKEYLOGFILE</envar> behaves the same as the <xref
+      linkend="libpq-connect-sslkeylogfile"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 8f211821eb..806688abc0 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -366,6 +366,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"Load-Balance-Hosts", "", 8,	/* sizeof("disable") = 8 */
 	offsetof(struct pg_conn, load_balance_hosts)},
 
+	{"sslkeylogfile", "SSLKEYLOGFILE",
+		"", NULL,
+		"SSL-Key-Log-File", "", 0,	/* sizeof("") = 0 */
+	offsetof(struct pg_conn, sslkeylogfile)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 5bb9d9779d..4c8f86cd31 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -57,6 +57,7 @@
  * include <wincrypt.h>, but some other Windows headers do.)
  */
 #include "common/openssl.h"
+#include <openssl/ssl.h>
 #include <openssl/conf.h>
 #ifdef USE_SSL_ENGINE
 #include <openssl/engine.h>
@@ -86,6 +87,7 @@ static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
 static int	ssl_protocol_version_to_openssl(const char *protocol);
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line);
 
 /* ------------------------------------------------------------ */
 /*			 Procedures common to all secure sessions			*/
@@ -684,6 +686,27 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 /* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
 static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
 
+/* This is a callback that writes to a given SSLKEYLOGFILE file */
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line) {
+	FILE *log_file;
+	PGconn *conn = SSL_get_app_data(ssl);
+	if (conn == NULL)
+		return;
+
+	log_file = fopen(conn->sslkeylogfile, "a");
+	if (log_file == NULL) {
+		libpq_append_conn_error(conn, "could not open ssl key log file");
+		return;
+	}
+
+	if (chmod(conn->sslkeylogfile, 0600) == -1) {
+		libpq_append_conn_error(conn, "could not chmod ssl key log file");
+		return;
+	}
+	fprintf(log_file, "%s\n", line);
+	fclose(log_file);
+}
+
 /*
  *	Create per-connection SSL object, and load the client certificate,
  *	private key, and trusted CA certs.
@@ -1000,6 +1023,9 @@ initialize_SSL(PGconn *conn)
 	}
 	conn->ssl_in_use = true;
 
+	if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
+		SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
+
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
 	 * as we have created the SSL object, and it will stick around for as long
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4a5a7c8b5e..57b8f7e602 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -428,6 +428,7 @@ struct pg_conn
 	char	   *target_session_attrs;	/* desired session properties */
 	char	   *require_auth;	/* name of the expected auth method */
 	char	   *load_balance_hosts; /* load balance over hosts */
+	char	   *sslkeylogfile;	/* where should the client write ssl key logs */
 
 	bool		cancelRequest;	/* true if this connection is used to send a
 								 * cancel request, instead of being a normal
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index 9c83d93f79..eda8c094ab 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -129,6 +129,7 @@ BEGIN
 	  PGSSLCRL
 	  PGSSLCRLDIR
 	  PGSSLKEY
+	  PGSSLKEYLOGFILE
 	  PGSSLMAXPROTOCOLVERSION
 	  PGSSLMINPROTOCOLVERSION
 	  PGSSLMODE
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index cbef6d48d3..6e3dfc11af 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -830,6 +830,7 @@ initialize_environment(void)
 		unsetenv("PGSSLCRL");
 		unsetenv("PGSSLCRLDIR");
 		unsetenv("PGSSLKEY");
+		unsetenv("PGSSLKEYLOGFILE");
 		unsetenv("PGSSLMAXPROTOCOLVERSION");
 		unsetenv("PGSSLMINPROTOCOLVERSION");
 		unsetenv("PGSSLMODE");
-- 
2.47.1

#6Daniel Gustafsson
daniel@yesql.se
In reply to: Abhishek Chanda (#5)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 10 Jan 2025, at 04:59, Abhishek Chanda <abhishek.becs@gmail.com> wrote:

Thanks for the new version.

+ {"sslkeylogfile", "SSLKEYLOGFILE",
The env var should be PGSSLKEYLOGFILE with the PG prefix.

3. Added docs and tests

There is no test in the attached patch, did you write one but forgot to git add
it before committing locally?

--
Daniel Gustafsson

#7Abhishek Chanda
abhishek.becs@gmail.com
In reply to: Daniel Gustafsson (#6)
1 attachment(s)
Re: Adding support for SSLKEYLOGFILE in the frontend

Thanks, Daniel. Here is an updated patch with all previous changes,
added a simple connection test and another check to make sure file
mode is correct, and the env var fix. Please let me know if anything
needs to be changed. I tested this locally using meson running all TAP
tests, and also manually.

Thanks
Abhishek

On Fri, Jan 10, 2025 at 9:29 AM Daniel Gustafsson <daniel@yesql.se> wrote:

On 10 Jan 2025, at 04:59, Abhishek Chanda <abhishek.becs@gmail.com> wrote:

Thanks for the new version.

+ {"sslkeylogfile", "SSLKEYLOGFILE",
The env var should be PGSSLKEYLOGFILE with the PG prefix.

3. Added docs and tests

There is no test in the attached patch, did you write one but forgot to git add
it before committing locally?

--
Daniel Gustafsson

--
Thanks and regards
Abhishek Chanda

Attachments:

v3-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchapplication/octet-stream; name=v3-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchDownload
From be9f369f20fe21b6d42f896dadf92115c987f739 Mon Sep 17 00:00:00 2001
From: Abhishek Chanda <abhishek.becs@gmail.com>
Date: Tue, 7 Jan 2025 21:54:08 -0600
Subject: [PATCH v3] Add support for dumping SSL keylog to a file

This patch adds a new connection parameter which is used by libpq
to write keys used in a SSL context
---
 .gitignore                               |  1 +
 doc/src/sgml/libpq.sgml                  | 22 ++++++++++++
 src/interfaces/libpq/fe-connect.c        |  5 +++
 src/interfaces/libpq/fe-secure-openssl.c | 26 ++++++++++++++
 src/interfaces/libpq/libpq-int.h         |  1 +
 src/test/perl/PostgreSQL/Test/Utils.pm   | 43 ++++++++++++++++++++++++
 src/test/regress/pg_regress.c            |  1 +
 src/test/ssl/t/001_ssltests.pl           |  7 ++++
 8 files changed, 106 insertions(+)

diff --git a/.gitignore b/.gitignore
index 4e911395fe..025d6115da 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,7 @@ win32ver.rc
 *.exe
 lib*dll.def
 lib*.pc
+./src/test/ssl/key.txt
 
 # Local excludes in root directory
 /GNUmakefile
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 105b22b317..e2d36efe21 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1909,6 +1909,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
+      <term><literal>sslkeylogfile</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the location where libpq will log keys
+        used in this SSL context. This is useful for debugging postgres
+        protocol using tools like wireshark. This parameter is ignored if an
+        SSL connection is not made.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslpassword" xreflabel="sslpassword">
       <term><literal>sslpassword</literal></term>
       <listitem>
@@ -9032,6 +9044,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGSSLKEYLOGFILE</envar></primary>
+      </indexterm>
+      <envar>PGSSLKEYLOGFILE</envar> behaves the same as the <xref
+      linkend="libpq-connect-sslkeylogfile"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 8f211821eb..19d2e03050 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -366,6 +366,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"Load-Balance-Hosts", "", 8,	/* sizeof("disable") = 8 */
 	offsetof(struct pg_conn, load_balance_hosts)},
 
+	{"sslkeylogfile", "PGSSLKEYLOGFILE",
+		"", NULL,
+		"SSL-Key-Log-File", "", 0,	/* sizeof("") = 0 */
+	offsetof(struct pg_conn, sslkeylogfile)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 5bb9d9779d..cbd0fe53ac 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -57,6 +57,7 @@
  * include <wincrypt.h>, but some other Windows headers do.)
  */
 #include "common/openssl.h"
+#include <openssl/ssl.h>
 #include <openssl/conf.h>
 #ifdef USE_SSL_ENGINE
 #include <openssl/engine.h>
@@ -86,6 +87,7 @@ static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
 static int	ssl_protocol_version_to_openssl(const char *protocol);
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line);
 
 /* ------------------------------------------------------------ */
 /*			 Procedures common to all secure sessions			*/
@@ -684,6 +686,27 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 /* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
 static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
 
+/* This is a callback that writes to a given ssl key log file */
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line) {
+	FILE *log_file;
+	PGconn *conn = SSL_get_app_data(ssl);
+	if (conn == NULL)
+		return;
+
+	log_file = fopen(conn->sslkeylogfile, "a");
+	if (log_file == NULL) {
+		libpq_append_conn_error(conn, "could not open ssl key log file");
+		return;
+	}
+
+	if (chmod(conn->sslkeylogfile, 0600) == -1) {
+		libpq_append_conn_error(conn, "could not chmod ssl key log file");
+		return;
+	}
+	fprintf(log_file, "%s\n", line);
+	fclose(log_file);
+}
+
 /*
  *	Create per-connection SSL object, and load the client certificate,
  *	private key, and trusted CA certs.
@@ -1000,6 +1023,9 @@ initialize_SSL(PGconn *conn)
 	}
 	conn->ssl_in_use = true;
 
+	if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
+		SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
+
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
 	 * as we have created the SSL object, and it will stick around for as long
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 4a5a7c8b5e..57b8f7e602 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -428,6 +428,7 @@ struct pg_conn
 	char	   *target_session_attrs;	/* desired session properties */
 	char	   *require_auth;	/* name of the expected auth method */
 	char	   *load_balance_hosts; /* load balance over hosts */
+	char	   *sslkeylogfile;	/* where should the client write ssl key logs */
 
 	bool		cancelRequest;	/* true if this connection is used to send a
 								 * cancel request, instead of being a normal
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index 9c83d93f79..e5b082bcf5 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -67,6 +67,7 @@ our @EXPORT = qw(
   slurp_file
   append_to_file
   string_replace_file
+  check_file_mode
   check_mode_recursive
   chmod_recursive
   check_pg_config
@@ -129,6 +130,7 @@ BEGIN
 	  PGSSLCRL
 	  PGSSLCRLDIR
 	  PGSSLKEY
+	  PGSSLKEYLOGFILE
 	  PGSSLMAXPROTOCOLVERSION
 	  PGSSLMINPROTOCOLVERSION
 	  PGSSLMODE
@@ -586,6 +588,47 @@ sub string_replace_file
 
 =pod
 
+=item check_file_mode(path, expected_file_mode)
+
+Check that the given file exists and mode match the expected values.
+
+=cut
+
+sub check_file_mode {
+    my ($path, $expected_file_mode) = @_;
+
+    unless (-e $path) {
+        die "Error: File '$path' does not exist\n";
+    }
+
+    my $file_stat = stat($path);
+    unless (defined($file_stat)) {
+        my $is_ENOENT = $!{ENOENT};
+        my $msg = "unable to stat $path: $!";
+        if ($is_ENOENT) {
+            warn $msg;
+            return 0;
+        }
+        else {
+            die $msg;
+        }
+    }
+
+    my $file_mode = S_IMODE($file_stat->mode);
+
+    if (S_ISREG($file_stat->mode)) {
+        if ($file_mode != $expected_file_mode) {
+            print STDERR sprintf("%s mode must be %04o\n", 
+                               $path, $expected_file_mode);
+            return 0;
+        }
+    }
+    
+    return 1;
+}
+
+=pod
+
 =item check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
 
 Check that all file/dir modes in a directory match the expected values,
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index cbef6d48d3..6e3dfc11af 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -830,6 +830,7 @@ initialize_environment(void)
 		unsetenv("PGSSLCRL");
 		unsetenv("PGSSLCRLDIR");
 		unsetenv("PGSSLKEY");
+		unsetenv("PGSSLKEYLOGFILE");
 		unsetenv("PGSSLMAXPROTOCOLVERSION");
 		unsetenv("PGSSLMINPROTOCOLVERSION");
 		unsetenv("PGSSLMODE");
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index c4a8dbb001..5e4ba8dffa 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -147,6 +147,13 @@ my $default_ssl_connstr =
 $common_connstr =
   "$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
 
+# Connect should work with a given sslkeylogfile
+$node->connect_ok(
+	"$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=key.txt sslmode=require",
+	"connect with server root certand sslkeylogfile=key.txt");
+
+ok(check_file_mode("key.txt", 0600), 'check key.txt mode');
+
 # The server should not accept non-SSL connections.
 $node->connect_fails(
 	"$common_connstr sslmode=disable",
-- 
2.47.1

#8Abhishek Chanda
abhishek.becs@gmail.com
In reply to: Abhishek Chanda (#7)
1 attachment(s)
Re: Adding support for SSLKEYLOGFILE in the frontend

Hi all,

Please find attached a new version of this patch. I rebased on master,
added better error reporting and skipped the permissions check on
windows. Please let me know if this needs any changes. I tested this
locally using meson running all TAP tests.

Thanks

On Fri, Jan 10, 2025 at 5:16 PM Abhishek Chanda <abhishek.becs@gmail.com> wrote:

Thanks, Daniel. Here is an updated patch with all previous changes,
added a simple connection test and another check to make sure file
mode is correct, and the env var fix. Please let me know if anything
needs to be changed. I tested this locally using meson running all TAP
tests, and also manually.

Thanks
Abhishek

On Fri, Jan 10, 2025 at 9:29 AM Daniel Gustafsson <daniel@yesql.se> wrote:

On 10 Jan 2025, at 04:59, Abhishek Chanda <abhishek.becs@gmail.com> wrote:

Thanks for the new version.

+ {"sslkeylogfile", "SSLKEYLOGFILE",
The env var should be PGSSLKEYLOGFILE with the PG prefix.

3. Added docs and tests

There is no test in the attached patch, did you write one but forgot to git add
it before committing locally?

--
Daniel Gustafsson

--
Thanks and regards
Abhishek Chanda

--
Thanks and regards
Abhishek Chanda

Attachments:

v4-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchapplication/octet-stream; name=v4-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchDownload
From eb91afc87462d0054a09a988b6c7b4678b7a621a Mon Sep 17 00:00:00 2001
From: Abhishek Chanda <abhishek.becs@gmail.com>
Date: Tue, 7 Jan 2025 21:54:08 -0600
Subject: [PATCH v4] Add support for dumping SSL keylog to a file

This patch adds a new connection parameter which is used by libpq
to write keys used in a SSL context
---
 .gitignore                               |  1 +
 doc/src/sgml/libpq.sgml                  | 22 ++++++++++++
 src/interfaces/libpq/fe-connect.c        |  5 +++
 src/interfaces/libpq/fe-secure-openssl.c | 26 ++++++++++++++
 src/interfaces/libpq/libpq-int.h         |  1 +
 src/test/perl/PostgreSQL/Test/Utils.pm   | 46 ++++++++++++++++++++++++
 src/test/regress/pg_regress.c            |  1 +
 src/test/ssl/t/001_ssltests.pl           |  7 ++++
 8 files changed, 109 insertions(+)

diff --git a/.gitignore b/.gitignore
index 4e911395fe..025d6115da 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,7 @@ win32ver.rc
 *.exe
 lib*dll.def
 lib*.pc
+./src/test/ssl/key.txt
 
 # Local excludes in root directory
 /GNUmakefile
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index c49e975b08..1573cd865a 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1909,6 +1909,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
+      <term><literal>sslkeylogfile</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the location where libpq will log keys
+        used in this SSL context. This is useful for debugging postgres
+        protocol using tools like wireshark. This parameter is ignored if an
+        SSL connection is not made.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslpassword" xreflabel="sslpassword">
       <term><literal>sslpassword</literal></term>
       <listitem>
@@ -9060,6 +9072,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGSSLKEYLOGFILE</envar></primary>
+      </indexterm>
+      <envar>PGSSLKEYLOGFILE</envar> behaves the same as the <xref
+      linkend="libpq-connect-sslkeylogfile"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 85d1ca2864..9172bd4752 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -373,6 +373,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 	{"scram_server_key", NULL, NULL, NULL, "SCRAM-Server-Key", "D", SCRAM_MAX_KEY_LEN * 2,
 	offsetof(struct pg_conn, scram_server_key)},
 
+	{"sslkeylogfile", "PGSSLKEYLOGFILE",
+		"", NULL,
+		"SSL-Key-Log-File", "", 0,	/* sizeof("") = 0 */
+	offsetof(struct pg_conn, sslkeylogfile)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 5bb9d9779d..326257ce47 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -57,6 +57,7 @@
  * include <wincrypt.h>, but some other Windows headers do.)
  */
 #include "common/openssl.h"
+#include <openssl/ssl.h>
 #include <openssl/conf.h>
 #ifdef USE_SSL_ENGINE
 #include <openssl/engine.h>
@@ -86,6 +87,7 @@ static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
 static int	ssl_protocol_version_to_openssl(const char *protocol);
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line);
 
 /* ------------------------------------------------------------ */
 /*			 Procedures common to all secure sessions			*/
@@ -684,6 +686,27 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 /* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
 static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
 
+/* This is a callback that writes to a given ssl key log file */
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line) {
+	FILE *log_file;
+	PGconn *conn = SSL_get_app_data(ssl);
+	if (conn == NULL)
+		return;
+
+	log_file = fopen(conn->sslkeylogfile, "a");
+	if (log_file == NULL) {
+		libpq_append_conn_error(conn, "could not open ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
+		return;
+	}
+
+	if (chmod(conn->sslkeylogfile, 0600) == -1) {
+		libpq_append_conn_error(conn, "could not chmod ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
+		return;
+	}
+	fprintf(log_file, "%s\n", line);
+	fclose(log_file);
+}
+
 /*
  *	Create per-connection SSL object, and load the client certificate,
  *	private key, and trusted CA certs.
@@ -1000,6 +1023,9 @@ initialize_SSL(PGconn *conn)
 	}
 	conn->ssl_in_use = true;
 
+	if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
+		SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
+
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
 	 * as we have created the SSL object, and it will stick around for as long
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 2546f9f8a5..f35cd538cf 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -432,6 +432,7 @@ struct pg_conn
 	char	   *load_balance_hosts; /* load balance over hosts */
 	char	   *scram_client_key;	/* base64-encoded SCRAM client key */
 	char	   *scram_server_key;	/* base64-encoded SCRAM server key */
+	char	   *sslkeylogfile;	/* where should the client write ssl key logs */
 
 	bool		cancelRequest;	/* true if this connection is used to send a
 								 * cancel request, instead of being a normal
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index efe0321a4e..65f7ed764a 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -68,6 +68,7 @@ our @EXPORT = qw(
   slurp_file
   append_to_file
   string_replace_file
+  check_file_mode
   check_mode_recursive
   chmod_recursive
   check_pg_config
@@ -131,6 +132,7 @@ BEGIN
 	  PGSSLCRL
 	  PGSSLCRLDIR
 	  PGSSLKEY
+	  PGSSLKEYLOGFILE
 	  PGSSLMAXPROTOCOLVERSION
 	  PGSSLMINPROTOCOLVERSION
 	  PGSSLMODE
@@ -588,6 +590,50 @@ sub string_replace_file
 
 =pod
 
+=item check_file_mode(path, expected_file_mode)
+
+Check that the given file exists and mode match the expected values.
+
+=cut
+
+sub check_file_mode {
+    my ($path, $expected_file_mode) = @_;
+
+    unless (-e $path) {
+        die "Error: File '$path' does not exist\n";
+    }
+
+    my $file_stat = stat($path);
+    unless (defined($file_stat)) {
+        my $is_ENOENT = $!{ENOENT};
+        my $msg = "unable to stat $path: $!";
+        if ($is_ENOENT) {
+            warn $msg;
+            return 0;
+        }
+        else {
+            die $msg;
+        }
+    }
+
+    # Skip file mode check on Windows
+    return 1 if $windows_os;
+
+    my $file_mode = S_IMODE($file_stat->mode);
+
+    if (S_ISREG($file_stat->mode)) {
+        if ($file_mode != $expected_file_mode) {
+            print STDERR sprintf("%s mode must be %04o\n",
+                               $path, $expected_file_mode);
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+=pod
+
 =item check_mode_recursive(dir, expected_dir_mode, expected_file_mode, ignore_list)
 
 Check that all file/dir modes in a directory match the expected values,
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 5d85dcc62f..381a7266cb 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -834,6 +834,7 @@ initialize_environment(void)
 		unsetenv("PGSSLCRL");
 		unsetenv("PGSSLCRLDIR");
 		unsetenv("PGSSLKEY");
+		unsetenv("PGSSLKEYLOGFILE");
 		unsetenv("PGSSLMAXPROTOCOLVERSION");
 		unsetenv("PGSSLMINPROTOCOLVERSION");
 		unsetenv("PGSSLMODE");
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 5422511d4a..da4a591393 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -147,6 +147,13 @@ my $default_ssl_connstr =
 $common_connstr =
   "$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
 
+# Connect should work with a given sslkeylogfile
+$node->connect_ok(
+	"$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=key.txt sslmode=require",
+	"connect with server root certand sslkeylogfile=key.txt");
+
+ok(check_file_mode("key.txt", 0600), 'check key.txt mode');
+
 # The server should not accept non-SSL connections.
 $node->connect_fails(
 	"$common_connstr sslmode=disable",
-- 
2.34.1

#9Daniel Gustafsson
daniel@yesql.se
In reply to: Abhishek Chanda (#8)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 20 Feb 2025, at 04:36, Abhishek Chanda <abhishek.becs@gmail.com> wrote:
Please find attached a new version of this patch. I rebased on master,
added better error reporting and skipped the permissions check on
windows. Please let me know if this needs any changes. I tested this
locally using meson running all TAP tests.

Thanks for the new version, a few comments on this one from reading:

+./src/test/ssl/key.txt
The toplevel .gitignore should not be used for transient test output, there is
a .gitignore in src/test/ssl for that. The key.txt file should also be placed
in PostgreSQL::Test::Utils::tempdir and then cleaned up after the test.

+ <varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
The documentation should state that the file will use the NSS format.

+       if (log_file == NULL) {
+               libpq_append_conn_error(conn, "could not open ssl key log ...
+       }
This raise an error for not being able to operate on the file, but it doesn't
error out from the function and instead keeps going with more operations on the
file which couldn't be opened.

+ if (chmod(conn->sslkeylogfile, 0600) == -1) {
Rather than opening and try to set permissions separately, why not use open(2)
and set the umask? We probably also want to set O_NOFOLLOW.

+       if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
+               SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
This callback does exist in OpenSSL 1.1.1 but it's only available in LibreSSL
from OpenBSD 7.1 and onwards where we support 7.0 as the earliest version.
This feature seems like a good enough reason to bump the minimum LibreSSL
version, and 7.1 is well out support (7.5 goes EOL next), but it would need to
get done here.

+ # Skip file mode check on Windows
+ return 1 if $windows_os;
It should be up to each individual test to determine what to skip, not the
library code (and it should use SKIP:). src/test/ssl/t/001_ssltests.pl is an
example which skips permissions checks on Windows. Also, I'm not sure we need
a generic function in the testlibrary for something so basic.

+            print STDERR sprintf("%s mode must be %04o\n",
+                               $path, $expected_file_mode);
Test code should not print anything to stdout/stderr, it should use the TAP
compliant methods like diag() etc for this.

+ "connect with server root certand sslkeylogfile=key.txt");
s/certand/cert and/ ?

--
Daniel Gustafsson

#10Abhishek Chanda
abhishek.becs@gmail.com
In reply to: Daniel Gustafsson (#9)
1 attachment(s)
Re: Adding support for SSLKEYLOGFILE in the frontend

Thanks for the review, Daniel. Please find v5 of this patch attached.
I tried to bump up the minimum OpenBSD version in installation.sgml,
do I need to add this anywhere else? Please let me know if this needs
anything else.

Thanks

On Thu, Feb 20, 2025 at 4:25 PM Daniel Gustafsson <daniel@yesql.se> wrote:

On 20 Feb 2025, at 04:36, Abhishek Chanda <abhishek.becs@gmail.com> wrote:
Please find attached a new version of this patch. I rebased on master,
added better error reporting and skipped the permissions check on
windows. Please let me know if this needs any changes. I tested this
locally using meson running all TAP tests.

Thanks for the new version, a few comments on this one from reading:

+./src/test/ssl/key.txt
The toplevel .gitignore should not be used for transient test output, there is
a .gitignore in src/test/ssl for that. The key.txt file should also be placed
in PostgreSQL::Test::Utils::tempdir and then cleaned up after the test.

+ <varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
The documentation should state that the file will use the NSS format.

+       if (log_file == NULL) {
+               libpq_append_conn_error(conn, "could not open ssl key log ...
+       }
This raise an error for not being able to operate on the file, but it doesn't
error out from the function and instead keeps going with more operations on the
file which couldn't be opened.

+ if (chmod(conn->sslkeylogfile, 0600) == -1) {
Rather than opening and try to set permissions separately, why not use open(2)
and set the umask? We probably also want to set O_NOFOLLOW.

+       if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
+               SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
This callback does exist in OpenSSL 1.1.1 but it's only available in LibreSSL
from OpenBSD 7.1 and onwards where we support 7.0 as the earliest version.
This feature seems like a good enough reason to bump the minimum LibreSSL
version, and 7.1 is well out support (7.5 goes EOL next), but it would need to
get done here.

+ # Skip file mode check on Windows
+ return 1 if $windows_os;
It should be up to each individual test to determine what to skip, not the
library code (and it should use SKIP:). src/test/ssl/t/001_ssltests.pl is an
example which skips permissions checks on Windows. Also, I'm not sure we need
a generic function in the testlibrary for something so basic.

+            print STDERR sprintf("%s mode must be %04o\n",
+                               $path, $expected_file_mode);
Test code should not print anything to stdout/stderr, it should use the TAP
compliant methods like diag() etc for this.

+ "connect with server root certand sslkeylogfile=key.txt");
s/certand/cert and/ ?

--
Daniel Gustafsson

--
Thanks and regards
Abhishek Chanda

Attachments:

v5-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchapplication/octet-stream; name=v5-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchDownload
From c52d5d9db11fa682e99defa22ff64520caf6f5aa Mon Sep 17 00:00:00 2001
From: Abhishek Chanda <abhishek.becs@gmail.com>
Date: Tue, 7 Jan 2025 21:54:08 -0600
Subject: [PATCH v5] Add support for dumping SSL keylog to a file

This patch adds a new connection parameter which is used by libpq
to write keys used in a SSL context
---
 doc/src/sgml/installation.sgml           |  2 +-
 doc/src/sgml/libpq.sgml                  | 22 ++++++++++++++++
 src/interfaces/libpq/fe-connect.c        |  5 ++++
 src/interfaces/libpq/fe-secure-openssl.c | 33 ++++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h         |  1 +
 src/test/perl/PostgreSQL/Test/Utils.pm   |  1 +
 src/test/regress/pg_regress.c            |  1 +
 src/test/ssl/t/001_ssltests.pl           | 26 +++++++++++++++++++
 8 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index e076cefa3b..39fd16651b 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -301,7 +301,7 @@
       Additionally, <productname>LibreSSL</productname> is supported using the
       <productname>OpenSSL</productname> compatibility layer.  The minimum
       required version is 3.4 (from <systemitem class="osname">OpenBSD</systemitem>
-      version 7.0).
+      version 7.5).
      </para>
     </listitem>
 
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 8fa0515c6a..31b711004a 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1918,6 +1918,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
+      <term><literal>sslkeylogfile</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the location where libpq will log keys
+        used in this SSL context. This is useful for debugging postgres
+        protocol using tools like wireshark. This parameter is ignored if an
+        SSL connection is not made. Keys are logged in the NSS format.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslpassword" xreflabel="sslpassword">
       <term><literal>sslpassword</literal></term>
       <listitem>
@@ -9170,6 +9182,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGSSLKEYLOGFILE</envar></primary>
+      </indexterm>
+      <envar>PGSSLKEYLOGFILE</envar> behaves the same as the <xref
+      linkend="libpq-connect-sslkeylogfile"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index d5051f5e82..180314ae73 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -391,6 +391,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"OAuth-Scope", "", 15,
 	offsetof(struct pg_conn, oauth_scope)},
 
+	{"sslkeylogfile", "PGSSLKEYLOGFILE",
+		"", NULL,
+		"SSL-Key-Log-File", "", 0,	/* sizeof("") = 0 */
+	offsetof(struct pg_conn, sslkeylogfile)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 5bb9d9779d..9d5dcaa16f 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -57,6 +57,7 @@
  * include <wincrypt.h>, but some other Windows headers do.)
  */
 #include "common/openssl.h"
+#include <openssl/ssl.h>
 #include <openssl/conf.h>
 #ifdef USE_SSL_ENGINE
 #include <openssl/engine.h>
@@ -86,6 +87,7 @@ static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
 static int	ssl_protocol_version_to_openssl(const char *protocol);
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line);
 
 /* ------------------------------------------------------------ */
 /*			 Procedures common to all secure sessions			*/
@@ -684,6 +686,34 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 /* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
 static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
 
+/* This is a callback that writes to a given ssl key log file */
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line) {
+	int fd;
+	mode_t old_umask;
+	ssize_t bytes_written;
+	PGconn *conn = SSL_get_app_data(ssl);
+
+	if (conn == NULL)
+		return;
+
+	old_umask = umask(077);
+	fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, 0600);
+	umask(old_umask);
+
+	if (fd == -1) {
+		libpq_append_conn_error(conn, "could not open ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
+		return;
+	}
+
+	bytes_written = dprintf(fd, "%s\n", line);
+	if (bytes_written < 0) {
+		libpq_append_conn_error(conn, "could not write to ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
+		close(fd);
+		return;
+	}
+	close(fd);
+}
+
 /*
  *	Create per-connection SSL object, and load the client certificate,
  *	private key, and trusted CA certs.
@@ -1000,6 +1030,9 @@ initialize_SSL(PGconn *conn)
 	}
 	conn->ssl_in_use = true;
 
+	if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
+		SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
+
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
 	 * as we have created the SSL object, and it will stick around for as long
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index f36f7f19d5..174b1315b8 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -432,6 +432,7 @@ struct pg_conn
 	char	   *load_balance_hosts; /* load balance over hosts */
 	char	   *scram_client_key;	/* base64-encoded SCRAM client key */
 	char	   *scram_server_key;	/* base64-encoded SCRAM server key */
+	char	   *sslkeylogfile;	/* where should the client write ssl key logs */
 
 	bool		cancelRequest;	/* true if this connection is used to send a
 								 * cancel request, instead of being a normal
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index efe0321a4e..fedae5098a 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -131,6 +131,7 @@ BEGIN
 	  PGSSLCRL
 	  PGSSLCRLDIR
 	  PGSSLKEY
+	  PGSSLKEYLOGFILE
 	  PGSSLMAXPROTOCOLVERSION
 	  PGSSLMINPROTOCOLVERSION
 	  PGSSLMODE
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 5d85dcc62f..381a7266cb 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -834,6 +834,7 @@ initialize_environment(void)
 		unsetenv("PGSSLCRL");
 		unsetenv("PGSSLCRLDIR");
 		unsetenv("PGSSLKEY");
+		unsetenv("PGSSLKEYLOGFILE");
 		unsetenv("PGSSLMAXPROTOCOLVERSION");
 		unsetenv("PGSSLMINPROTOCOLVERSION");
 		unsetenv("PGSSLMODE");
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 5422511d4a..ecf9bf7dd9 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -147,6 +147,32 @@ my $default_ssl_connstr =
 $common_connstr =
   "$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
 
+my $tempdir = PostgreSQL::Test::Utils::tempdir;
+
+# Connect should work with a given sslkeylogfile
+$node->connect_ok(
+        "$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/key.txt sslmode=require",
+        "connect with server root cert and sslkeylogfile=$tempdir/key.txt");
+
+# Verify the key file exists
+ok(-f "$tempdir/key.txt", "key log file exists");
+
+# Skip permission checks on Windows/Cygwin
+SKIP:
+{
+        skip "Permissions check not enforced on Windows", 1
+        if ($windows_os || $Config::Config{osname} eq 'cygwin');
+
+        my $mode = (stat("$tempdir/key.txt"))[2];
+        my $permissions_ok = 1;
+
+        if ($mode & 0006) {
+            $permissions_ok = 0;
+        }
+
+        ok($permissions_ok, "key log file is not world readble");
+}
+
 # The server should not accept non-SSL connections.
 $node->connect_fails(
 	"$common_connstr sslmode=disable",
-- 
2.34.1

#11Abhishek Chanda
abhishek.becs@gmail.com
In reply to: Abhishek Chanda (#10)
1 attachment(s)
Re: Adding support for SSLKEYLOGFILE in the frontend

Attached a v6 with O_NOFOLLOW removed, I noticed that it failed on
windows. Please let me know if I should do this in any other way that
is portable across platforms.

Thanks

On Thu, Feb 27, 2025 at 11:39 PM Abhishek Chanda
<abhishek.becs@gmail.com> wrote:

Thanks for the review, Daniel. Please find v5 of this patch attached.
I tried to bump up the minimum OpenBSD version in installation.sgml,
do I need to add this anywhere else? Please let me know if this needs
anything else.

Thanks

On Thu, Feb 20, 2025 at 4:25 PM Daniel Gustafsson <daniel@yesql.se> wrote:

On 20 Feb 2025, at 04:36, Abhishek Chanda <abhishek.becs@gmail.com> wrote:
Please find attached a new version of this patch. I rebased on master,
added better error reporting and skipped the permissions check on
windows. Please let me know if this needs any changes. I tested this
locally using meson running all TAP tests.

Thanks for the new version, a few comments on this one from reading:

+./src/test/ssl/key.txt
The toplevel .gitignore should not be used for transient test output, there is
a .gitignore in src/test/ssl for that. The key.txt file should also be placed
in PostgreSQL::Test::Utils::tempdir and then cleaned up after the test.

+ <varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
The documentation should state that the file will use the NSS format.

+       if (log_file == NULL) {
+               libpq_append_conn_error(conn, "could not open ssl key log ...
+       }
This raise an error for not being able to operate on the file, but it doesn't
error out from the function and instead keeps going with more operations on the
file which couldn't be opened.

+ if (chmod(conn->sslkeylogfile, 0600) == -1) {
Rather than opening and try to set permissions separately, why not use open(2)
and set the umask? We probably also want to set O_NOFOLLOW.

+       if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
+               SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
This callback does exist in OpenSSL 1.1.1 but it's only available in LibreSSL
from OpenBSD 7.1 and onwards where we support 7.0 as the earliest version.
This feature seems like a good enough reason to bump the minimum LibreSSL
version, and 7.1 is well out support (7.5 goes EOL next), but it would need to
get done here.

+ # Skip file mode check on Windows
+ return 1 if $windows_os;
It should be up to each individual test to determine what to skip, not the
library code (and it should use SKIP:). src/test/ssl/t/001_ssltests.pl is an
example which skips permissions checks on Windows. Also, I'm not sure we need
a generic function in the testlibrary for something so basic.

+            print STDERR sprintf("%s mode must be %04o\n",
+                               $path, $expected_file_mode);
Test code should not print anything to stdout/stderr, it should use the TAP
compliant methods like diag() etc for this.

+ "connect with server root certand sslkeylogfile=key.txt");
s/certand/cert and/ ?

--
Daniel Gustafsson

--
Thanks and regards
Abhishek Chanda

--
Thanks and regards
Abhishek Chanda

Attachments:

v6-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchapplication/octet-stream; name=v6-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchDownload
From 2f27659eadf81cd42781d41bedba564356bf917e Mon Sep 17 00:00:00 2001
From: Abhishek Chanda <abhishek.becs@gmail.com>
Date: Tue, 7 Jan 2025 21:54:08 -0600
Subject: [PATCH v6] Add support for dumping SSL keylog to a file

This patch adds a new connection parameter which is used by libpq
to write keys used in a SSL context
---
 doc/src/sgml/installation.sgml           |  2 +-
 doc/src/sgml/libpq.sgml                  | 22 ++++++++++++++++
 src/interfaces/libpq/fe-connect.c        |  5 ++++
 src/interfaces/libpq/fe-secure-openssl.c | 33 ++++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h         |  1 +
 src/test/perl/PostgreSQL/Test/Utils.pm   |  1 +
 src/test/regress/pg_regress.c            |  1 +
 src/test/ssl/t/001_ssltests.pl           | 26 +++++++++++++++++++
 8 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index e076cefa3b..39fd16651b 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -301,7 +301,7 @@
       Additionally, <productname>LibreSSL</productname> is supported using the
       <productname>OpenSSL</productname> compatibility layer.  The minimum
       required version is 3.4 (from <systemitem class="osname">OpenBSD</systemitem>
-      version 7.0).
+      version 7.5).
      </para>
     </listitem>
 
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 8fa0515c6a..31b711004a 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1918,6 +1918,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
+      <term><literal>sslkeylogfile</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the location where libpq will log keys
+        used in this SSL context. This is useful for debugging postgres
+        protocol using tools like wireshark. This parameter is ignored if an
+        SSL connection is not made. Keys are logged in the NSS format.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslpassword" xreflabel="sslpassword">
       <term><literal>sslpassword</literal></term>
       <listitem>
@@ -9170,6 +9182,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGSSLKEYLOGFILE</envar></primary>
+      </indexterm>
+      <envar>PGSSLKEYLOGFILE</envar> behaves the same as the <xref
+      linkend="libpq-connect-sslkeylogfile"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index d5051f5e82..180314ae73 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -391,6 +391,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"OAuth-Scope", "", 15,
 	offsetof(struct pg_conn, oauth_scope)},
 
+	{"sslkeylogfile", "PGSSLKEYLOGFILE",
+		"", NULL,
+		"SSL-Key-Log-File", "", 0,	/* sizeof("") = 0 */
+	offsetof(struct pg_conn, sslkeylogfile)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 5bb9d9779d..b0ca09b670 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -57,6 +57,7 @@
  * include <wincrypt.h>, but some other Windows headers do.)
  */
 #include "common/openssl.h"
+#include <openssl/ssl.h>
 #include <openssl/conf.h>
 #ifdef USE_SSL_ENGINE
 #include <openssl/engine.h>
@@ -86,6 +87,7 @@ static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
 static int	ssl_protocol_version_to_openssl(const char *protocol);
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line);
 
 /* ------------------------------------------------------------ */
 /*			 Procedures common to all secure sessions			*/
@@ -684,6 +686,34 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 /* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
 static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
 
+/* This is a callback that writes to a given ssl key log file */
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line) {
+	int fd;
+	mode_t old_umask;
+	ssize_t bytes_written;
+	PGconn *conn = SSL_get_app_data(ssl);
+
+	if (conn == NULL)
+		return;
+
+	old_umask = umask(077);
+	fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
+	umask(old_umask);
+
+	if (fd == -1) {
+		libpq_append_conn_error(conn, "could not open ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
+		return;
+	}
+
+	bytes_written = dprintf(fd, "%s\n", line);
+	if (bytes_written < 0) {
+		libpq_append_conn_error(conn, "could not write to ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
+		close(fd);
+		return;
+	}
+	close(fd);
+}
+
 /*
  *	Create per-connection SSL object, and load the client certificate,
  *	private key, and trusted CA certs.
@@ -1000,6 +1030,9 @@ initialize_SSL(PGconn *conn)
 	}
 	conn->ssl_in_use = true;
 
+	if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
+		SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
+
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
 	 * as we have created the SSL object, and it will stick around for as long
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index f36f7f19d5..174b1315b8 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -432,6 +432,7 @@ struct pg_conn
 	char	   *load_balance_hosts; /* load balance over hosts */
 	char	   *scram_client_key;	/* base64-encoded SCRAM client key */
 	char	   *scram_server_key;	/* base64-encoded SCRAM server key */
+	char	   *sslkeylogfile;	/* where should the client write ssl key logs */
 
 	bool		cancelRequest;	/* true if this connection is used to send a
 								 * cancel request, instead of being a normal
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index efe0321a4e..fedae5098a 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -131,6 +131,7 @@ BEGIN
 	  PGSSLCRL
 	  PGSSLCRLDIR
 	  PGSSLKEY
+	  PGSSLKEYLOGFILE
 	  PGSSLMAXPROTOCOLVERSION
 	  PGSSLMINPROTOCOLVERSION
 	  PGSSLMODE
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 5d85dcc62f..381a7266cb 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -834,6 +834,7 @@ initialize_environment(void)
 		unsetenv("PGSSLCRL");
 		unsetenv("PGSSLCRLDIR");
 		unsetenv("PGSSLKEY");
+		unsetenv("PGSSLKEYLOGFILE");
 		unsetenv("PGSSLMAXPROTOCOLVERSION");
 		unsetenv("PGSSLMINPROTOCOLVERSION");
 		unsetenv("PGSSLMODE");
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 5422511d4a..ecf9bf7dd9 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -147,6 +147,32 @@ my $default_ssl_connstr =
 $common_connstr =
   "$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
 
+my $tempdir = PostgreSQL::Test::Utils::tempdir;
+
+# Connect should work with a given sslkeylogfile
+$node->connect_ok(
+        "$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/key.txt sslmode=require",
+        "connect with server root cert and sslkeylogfile=$tempdir/key.txt");
+
+# Verify the key file exists
+ok(-f "$tempdir/key.txt", "key log file exists");
+
+# Skip permission checks on Windows/Cygwin
+SKIP:
+{
+        skip "Permissions check not enforced on Windows", 1
+        if ($windows_os || $Config::Config{osname} eq 'cygwin');
+
+        my $mode = (stat("$tempdir/key.txt"))[2];
+        my $permissions_ok = 1;
+
+        if ($mode & 0006) {
+            $permissions_ok = 0;
+        }
+
+        ok($permissions_ok, "key log file is not world readble");
+}
+
 # The server should not accept non-SSL connections.
 $node->connect_fails(
 	"$common_connstr sslmode=disable",
-- 
2.34.1

#12Daniel Gustafsson
daniel@yesql.se
In reply to: Abhishek Chanda (#11)
2 attachment(s)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 28 Feb 2025, at 07:20, Abhishek Chanda <abhishek.becs@gmail.com> wrote:

Attached a v6 with O_NOFOLLOW removed, I noticed that it failed on
windows. Please let me know if I should do this in any other way that
is portable across platforms.

Not sure if there is a portable way to can support.

required version is 3.4 (from <systemitem class="osname">OpenBSD</systemitem>
- version 7.0).
+ version 7.5).
Bumping the version needs a bit more infrastructure than this. Doing a bit
more research unveiled that there is no need to do this though since LibreSSL
doesn't support keylogging at all, they have only implemented stubs for
compatibility. I've added checks to autoconf and meson to identify the
capability and conditional compilation of the support. The tests are also
updated to reflect this.

- bytes_written = dprintf(fd, "%s\n", line);
We use write(2) everywhere so I've changed to patch to do the same.

The attached 0002 also contains documentation touchups and comments etc. 0001
is your patch from v6.

--
Daniel Gustafsson

Attachments:

v7-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchapplication/octet-stream; name=v7-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patch; x-unix-mode=0644Download
From 7f289395ce9966230f1f90baddda89b454c7a67a Mon Sep 17 00:00:00 2001
From: Abhishek Chanda <abhishek.becs@gmail.com>
Date: Tue, 7 Jan 2025 21:54:08 -0600
Subject: [PATCH v7 1/2] Add support for dumping SSL keylog to a file

This patch adds a new connection parameter which is used by libpq
to write keys used in a SSL context
---
 doc/src/sgml/installation.sgml           |  2 +-
 doc/src/sgml/libpq.sgml                  | 22 ++++++++++++++++
 src/interfaces/libpq/fe-connect.c        |  5 ++++
 src/interfaces/libpq/fe-secure-openssl.c | 33 ++++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h         |  1 +
 src/test/perl/PostgreSQL/Test/Utils.pm   |  1 +
 src/test/regress/pg_regress.c            |  1 +
 src/test/ssl/t/001_ssltests.pl           | 26 +++++++++++++++++++
 8 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index e076cefa3b9..39fd16651b9 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -301,7 +301,7 @@
       Additionally, <productname>LibreSSL</productname> is supported using the
       <productname>OpenSSL</productname> compatibility layer.  The minimum
       required version is 3.4 (from <systemitem class="osname">OpenBSD</systemitem>
-      version 7.0).
+      version 7.5).
      </para>
     </listitem>
 
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 8fa0515c6a0..31b711004aa 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1918,6 +1918,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
+      <term><literal>sslkeylogfile</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the location where libpq will log keys
+        used in this SSL context. This is useful for debugging postgres
+        protocol using tools like wireshark. This parameter is ignored if an
+        SSL connection is not made. Keys are logged in the NSS format.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslpassword" xreflabel="sslpassword">
       <term><literal>sslpassword</literal></term>
       <listitem>
@@ -9170,6 +9182,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGSSLKEYLOGFILE</envar></primary>
+      </indexterm>
+      <envar>PGSSLKEYLOGFILE</envar> behaves the same as the <xref
+      linkend="libpq-connect-sslkeylogfile"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index d5051f5e820..180314ae73d 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -391,6 +391,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"OAuth-Scope", "", 15,
 	offsetof(struct pg_conn, oauth_scope)},
 
+	{"sslkeylogfile", "PGSSLKEYLOGFILE",
+		"", NULL,
+		"SSL-Key-Log-File", "", 0,	/* sizeof("") = 0 */
+	offsetof(struct pg_conn, sslkeylogfile)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 5bb9d9779d8..b0ca09b6700 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -57,6 +57,7 @@
  * include <wincrypt.h>, but some other Windows headers do.)
  */
 #include "common/openssl.h"
+#include <openssl/ssl.h>
 #include <openssl/conf.h>
 #ifdef USE_SSL_ENGINE
 #include <openssl/engine.h>
@@ -86,6 +87,7 @@ static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
 static int	ssl_protocol_version_to_openssl(const char *protocol);
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line);
 
 /* ------------------------------------------------------------ */
 /*			 Procedures common to all secure sessions			*/
@@ -684,6 +686,34 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 /* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
 static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
 
+/* This is a callback that writes to a given ssl key log file */
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line) {
+	int fd;
+	mode_t old_umask;
+	ssize_t bytes_written;
+	PGconn *conn = SSL_get_app_data(ssl);
+
+	if (conn == NULL)
+		return;
+
+	old_umask = umask(077);
+	fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
+	umask(old_umask);
+
+	if (fd == -1) {
+		libpq_append_conn_error(conn, "could not open ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
+		return;
+	}
+
+	bytes_written = dprintf(fd, "%s\n", line);
+	if (bytes_written < 0) {
+		libpq_append_conn_error(conn, "could not write to ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
+		close(fd);
+		return;
+	}
+	close(fd);
+}
+
 /*
  *	Create per-connection SSL object, and load the client certificate,
  *	private key, and trusted CA certs.
@@ -1000,6 +1030,9 @@ initialize_SSL(PGconn *conn)
 	}
 	conn->ssl_in_use = true;
 
+	if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
+		SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
+
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
 	 * as we have created the SSL object, and it will stick around for as long
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index f36f7f19d58..174b1315b86 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -432,6 +432,7 @@ struct pg_conn
 	char	   *load_balance_hosts; /* load balance over hosts */
 	char	   *scram_client_key;	/* base64-encoded SCRAM client key */
 	char	   *scram_server_key;	/* base64-encoded SCRAM server key */
+	char	   *sslkeylogfile;	/* where should the client write ssl key logs */
 
 	bool		cancelRequest;	/* true if this connection is used to send a
 								 * cancel request, instead of being a normal
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index efe0321a4ef..fedae5098aa 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -131,6 +131,7 @@ BEGIN
 	  PGSSLCRL
 	  PGSSLCRLDIR
 	  PGSSLKEY
+	  PGSSLKEYLOGFILE
 	  PGSSLMAXPROTOCOLVERSION
 	  PGSSLMINPROTOCOLVERSION
 	  PGSSLMODE
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 5d85dcc62f0..381a7266cb9 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -834,6 +834,7 @@ initialize_environment(void)
 		unsetenv("PGSSLCRL");
 		unsetenv("PGSSLCRLDIR");
 		unsetenv("PGSSLKEY");
+		unsetenv("PGSSLKEYLOGFILE");
 		unsetenv("PGSSLMAXPROTOCOLVERSION");
 		unsetenv("PGSSLMINPROTOCOLVERSION");
 		unsetenv("PGSSLMODE");
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 5422511d4ab..ecf9bf7dd93 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -147,6 +147,32 @@ my $default_ssl_connstr =
 $common_connstr =
   "$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
 
+my $tempdir = PostgreSQL::Test::Utils::tempdir;
+
+# Connect should work with a given sslkeylogfile
+$node->connect_ok(
+        "$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/key.txt sslmode=require",
+        "connect with server root cert and sslkeylogfile=$tempdir/key.txt");
+
+# Verify the key file exists
+ok(-f "$tempdir/key.txt", "key log file exists");
+
+# Skip permission checks on Windows/Cygwin
+SKIP:
+{
+        skip "Permissions check not enforced on Windows", 1
+        if ($windows_os || $Config::Config{osname} eq 'cygwin');
+
+        my $mode = (stat("$tempdir/key.txt"))[2];
+        my $permissions_ok = 1;
+
+        if ($mode & 0006) {
+            $permissions_ok = 0;
+        }
+
+        ok($permissions_ok, "key log file is not world readble");
+}
+
 # The server should not accept non-SSL connections.
 $node->connect_fails(
 	"$common_connstr sslmode=disable",
-- 
2.39.3 (Apple Git-146)

v7-0002-Review-fixups.patchapplication/octet-stream; name=v7-0002-Review-fixups.patch; x-unix-mode=0644Download
From bfa6a26b4610258d510b49a3fbdc8a249a757576 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Mon, 3 Mar 2025 16:17:14 +0100
Subject: [PATCH v7 2/2] Review fixups

---
 configure.ac                             |  2 +-
 doc/src/sgml/installation.sgml           |  2 +-
 doc/src/sgml/libpq.sgml                  | 19 +++++++---
 meson.build                              |  1 +
 src/include/pg_config.h.in               |  3 ++
 src/interfaces/libpq/fe-secure-openssl.c | 46 ++++++++++++++++--------
 src/test/ssl/t/001_ssltests.pl           | 42 ++++++++++++----------
 7 files changed, 76 insertions(+), 39 deletions(-)

diff --git a/configure.ac b/configure.ac
index b6d02f5ecc7..c6f61f7e3a8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1370,7 +1370,7 @@ if test "$with_ssl" = openssl ; then
   # Function introduced in OpenSSL 1.0.2, not in LibreSSL.
   AC_CHECK_FUNCS([SSL_CTX_set_cert_cb])
   # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
-  AC_CHECK_FUNCS([X509_get_signature_info SSL_CTX_set_num_tickets])
+  AC_CHECK_FUNCS([X509_get_signature_info SSL_CTX_set_num_tickets SSL_CTX_set_keylog_callback])
   AC_DEFINE([USE_OPENSSL], 1, [Define to 1 to build with OpenSSL support. (--with-ssl=openssl)])
 elif test "$with_ssl" != no ; then
   AC_MSG_ERROR([--with-ssl must specify openssl])
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 39fd16651b9..e076cefa3b9 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -301,7 +301,7 @@
       Additionally, <productname>LibreSSL</productname> is supported using the
       <productname>OpenSSL</productname> compatibility layer.  The minimum
       required version is 3.4 (from <systemitem class="osname">OpenBSD</systemitem>
-      version 7.5).
+      version 7.0).
      </para>
     </listitem>
 
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 31b711004aa..cea8acd3ccf 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1922,10 +1922,21 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       <term><literal>sslkeylogfile</literal></term>
       <listitem>
        <para>
-        This parameter specifies the location where libpq will log keys
-        used in this SSL context. This is useful for debugging postgres
-        protocol using tools like wireshark. This parameter is ignored if an
-        SSL connection is not made. Keys are logged in the NSS format.
+        This parameter specifies the location where <literal>libpq</literal>
+        will log keys used in this SSL context.  This is useful for debugging
+        <productname>PostgreSQL</productname> protocol interations or client
+        connections using network inspection tools like
+        <productname>Wireshark</productname>.  This parameter is ignored if an
+        SSL connection is not made, or if <productname>LibreSSL</productname>
+        is used.  Keys are logged in the <productname>NSS</productname> format.
+
+        <warning>
+         <para>
+          Key logging will expose potentially sensitive information in the
+          keylog file and it should be handled with the same caution as
+          <xref linkend="libpq-connect-sslkey" /> files.
+         </para>
+        </warning>
        </para>
       </listitem>
      </varlistentry>
diff --git a/meson.build b/meson.build
index 13c13748e5d..07c51e7db58 100644
--- a/meson.build
+++ b/meson.build
@@ -1472,6 +1472,7 @@ if sslopt in ['auto', 'openssl']
       # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
       ['X509_get_signature_info'],
       ['SSL_CTX_set_num_tickets'],
+      ['SSL_CTX_set_keylog_callback'],
     ]
 
     are_openssl_funcs_complete = true
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index db6454090d2..64351a3adaa 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -361,6 +361,9 @@
 /* Define to 1 if you have the `SSL_CTX_set_ciphersuites' function. */
 #undef HAVE_SSL_CTX_SET_CIPHERSUITES
 
+/* Define to 1 if you have the `SSL_CTX_set_keylog_callback' function. */
+#undef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+
 /* Define to 1 if you have the `SSL_CTX_set_num_tickets' function. */
 #undef HAVE_SSL_CTX_SET_NUM_TICKETS
 
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index b0ca09b6700..fb6c99f4200 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -87,7 +87,6 @@ static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
 static int	ssl_protocol_version_to_openssl(const char *protocol);
-static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line);
 
 /* ------------------------------------------------------------ */
 /*			 Procedures common to all secure sessions			*/
@@ -686,12 +685,22 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 /* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
 static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
 
-/* This is a callback that writes to a given ssl key log file */
-static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line) {
-	int fd;
-	mode_t old_umask;
-	ssize_t bytes_written;
-	PGconn *conn = SSL_get_app_data(ssl);
+#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+/*
+ * SSL Key Logging callback
+ *
+ * This callback lets the user store all key material to a file for debugging
+ * purposes.  The file will be written using the NSS keylog format.  LibreSSL
+ * 3.5 introduce stub function to set the callback for OpenSSL compatibility,
+ * but the callback is never invoked.
+ */
+static void
+SSL_CTX_keylog_cb(const SSL *ssl, const char *line)
+{
+	int			fd;
+	mode_t		old_umask;
+	ssize_t		rc;
+	PGconn	   *conn = SSL_get_app_data(ssl);
 
 	if (conn == NULL)
 		return;
@@ -700,19 +709,24 @@ static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line) {
 	fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
 	umask(old_umask);
 
-	if (fd == -1) {
-		libpq_append_conn_error(conn, "could not open ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
+	if (fd == -1)
+	{
+		libpq_append_conn_error(conn, "could not open ssl key log file %s: %s",
+								conn->sslkeylogfile, pg_strerror(errno));
 		return;
 	}
 
-	bytes_written = dprintf(fd, "%s\n", line);
-	if (bytes_written < 0) {
-		libpq_append_conn_error(conn, "could not write to ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
-		close(fd);
-		return;
-	}
+	/* line is guaranteed by OpenSSL to be NUL terminated */
+	rc = write(fd, line, strlen(line));
+	if (rc < 0)
+		libpq_append_conn_error(conn, "could not write to ssl key log file %s: %s",
+								conn->sslkeylogfile, pg_strerror(errno));
+	else
+		rc = write(fd, "\n", 1);
+	(void) rc;					/* silence compiler warnings */
 	close(fd);
 }
+#endif
 
 /*
  *	Create per-connection SSL object, and load the client certificate,
@@ -1030,8 +1044,10 @@ initialize_SSL(PGconn *conn)
 	}
 	conn->ssl_in_use = true;
 
+#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
 	if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
 		SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
+#endif
 
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index ecf9bf7dd93..8cd66a56656 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -147,30 +147,36 @@ my $default_ssl_connstr =
 $common_connstr =
   "$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
 
-my $tempdir = PostgreSQL::Test::Utils::tempdir;
+SKIP:
+{
+	skip "Keylogging is not supported with LibreSSL" if $libressl;
 
-# Connect should work with a given sslkeylogfile
-$node->connect_ok(
-        "$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/key.txt sslmode=require",
-        "connect with server root cert and sslkeylogfile=$tempdir/key.txt");
+	my $tempdir = PostgreSQL::Test::Utils::tempdir;
 
-# Verify the key file exists
-ok(-f "$tempdir/key.txt", "key log file exists");
+	# Connect should work with a given sslkeylogfile
+	$node->connect_ok(
+		"$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/key.txt sslmode=require",
+		"connect with server root cert and sslkeylogfile=$tempdir/key.txt");
 
-# Skip permission checks on Windows/Cygwin
-SKIP:
-{
-        skip "Permissions check not enforced on Windows", 1
-        if ($windows_os || $Config::Config{osname} eq 'cygwin');
+	# Verify the key file exists
+	ok(-f "$tempdir/key.txt", "key log file exists");
+
+	# Skip permission checks on Windows/Cygwin
+  SKIP:
+	{
+		skip "Permissions check not enforced on Windows", 1
+		  if ($windows_os || $Config::Config{osname} eq 'cygwin');
 
-        my $mode = (stat("$tempdir/key.txt"))[2];
-        my $permissions_ok = 1;
+		my $mode = (stat("$tempdir/key.txt"))[2];
+		my $permissions_ok = 1;
 
-        if ($mode & 0006) {
-            $permissions_ok = 0;
-        }
+		if ($mode & 0006)
+		{
+			$permissions_ok = 0;
+		}
 
-        ok($permissions_ok, "key log file is not world readble");
+		ok($permissions_ok, "key log file is not world readble");
+	}
 }
 
 # The server should not accept non-SSL connections.
-- 
2.39.3 (Apple Git-146)

#13Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Gustafsson (#12)
2 attachment(s)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 3 Mar 2025, at 16:23, Daniel Gustafsson <daniel@yesql.se> wrote:

The attached 0002 also contains documentation touchups and comments etc. 0001
is your patch from v6.

I managed to misunderstand skip blocks in TAP tests in the 0002, so the
attached version fixes that. It has been failing on Debian in CI which I have
yet to look into.

--
Daniel Gustafsson

Attachments:

v8-0002-Review-fixups.patchapplication/octet-stream; name=v8-0002-Review-fixups.patch; x-unix-mode=0644Download
From 7257bd0c838240768cfcb021179d50976c845d05 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Mon, 3 Mar 2025 16:17:14 +0100
Subject: [PATCH v8 2/2] Review fixups

---
 configure.ac                             |  2 +-
 doc/src/sgml/installation.sgml           |  2 +-
 doc/src/sgml/libpq.sgml                  | 19 +++++++---
 meson.build                              |  1 +
 src/include/pg_config.h.in               |  3 ++
 src/interfaces/libpq/fe-secure-openssl.c | 46 ++++++++++++++++--------
 src/test/ssl/t/001_ssltests.pl           | 35 +++++++++---------
 7 files changed, 68 insertions(+), 40 deletions(-)

diff --git a/configure.ac b/configure.ac
index b6d02f5ecc7..c6f61f7e3a8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1370,7 +1370,7 @@ if test "$with_ssl" = openssl ; then
   # Function introduced in OpenSSL 1.0.2, not in LibreSSL.
   AC_CHECK_FUNCS([SSL_CTX_set_cert_cb])
   # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
-  AC_CHECK_FUNCS([X509_get_signature_info SSL_CTX_set_num_tickets])
+  AC_CHECK_FUNCS([X509_get_signature_info SSL_CTX_set_num_tickets SSL_CTX_set_keylog_callback])
   AC_DEFINE([USE_OPENSSL], 1, [Define to 1 to build with OpenSSL support. (--with-ssl=openssl)])
 elif test "$with_ssl" != no ; then
   AC_MSG_ERROR([--with-ssl must specify openssl])
diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index 39fd16651b9..e076cefa3b9 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -301,7 +301,7 @@
       Additionally, <productname>LibreSSL</productname> is supported using the
       <productname>OpenSSL</productname> compatibility layer.  The minimum
       required version is 3.4 (from <systemitem class="osname">OpenBSD</systemitem>
-      version 7.5).
+      version 7.0).
      </para>
     </listitem>
 
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 31b711004aa..cea8acd3ccf 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1922,10 +1922,21 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       <term><literal>sslkeylogfile</literal></term>
       <listitem>
        <para>
-        This parameter specifies the location where libpq will log keys
-        used in this SSL context. This is useful for debugging postgres
-        protocol using tools like wireshark. This parameter is ignored if an
-        SSL connection is not made. Keys are logged in the NSS format.
+        This parameter specifies the location where <literal>libpq</literal>
+        will log keys used in this SSL context.  This is useful for debugging
+        <productname>PostgreSQL</productname> protocol interations or client
+        connections using network inspection tools like
+        <productname>Wireshark</productname>.  This parameter is ignored if an
+        SSL connection is not made, or if <productname>LibreSSL</productname>
+        is used.  Keys are logged in the <productname>NSS</productname> format.
+
+        <warning>
+         <para>
+          Key logging will expose potentially sensitive information in the
+          keylog file and it should be handled with the same caution as
+          <xref linkend="libpq-connect-sslkey" /> files.
+         </para>
+        </warning>
        </para>
       </listitem>
      </varlistentry>
diff --git a/meson.build b/meson.build
index 13c13748e5d..07c51e7db58 100644
--- a/meson.build
+++ b/meson.build
@@ -1472,6 +1472,7 @@ if sslopt in ['auto', 'openssl']
       # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
       ['X509_get_signature_info'],
       ['SSL_CTX_set_num_tickets'],
+      ['SSL_CTX_set_keylog_callback'],
     ]
 
     are_openssl_funcs_complete = true
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index db6454090d2..64351a3adaa 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -361,6 +361,9 @@
 /* Define to 1 if you have the `SSL_CTX_set_ciphersuites' function. */
 #undef HAVE_SSL_CTX_SET_CIPHERSUITES
 
+/* Define to 1 if you have the `SSL_CTX_set_keylog_callback' function. */
+#undef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+
 /* Define to 1 if you have the `SSL_CTX_set_num_tickets' function. */
 #undef HAVE_SSL_CTX_SET_NUM_TICKETS
 
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index b0ca09b6700..fb6c99f4200 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -87,7 +87,6 @@ static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
 static int	ssl_protocol_version_to_openssl(const char *protocol);
-static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line);
 
 /* ------------------------------------------------------------ */
 /*			 Procedures common to all secure sessions			*/
@@ -686,12 +685,22 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 /* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
 static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
 
-/* This is a callback that writes to a given ssl key log file */
-static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line) {
-	int fd;
-	mode_t old_umask;
-	ssize_t bytes_written;
-	PGconn *conn = SSL_get_app_data(ssl);
+#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+/*
+ * SSL Key Logging callback
+ *
+ * This callback lets the user store all key material to a file for debugging
+ * purposes.  The file will be written using the NSS keylog format.  LibreSSL
+ * 3.5 introduce stub function to set the callback for OpenSSL compatibility,
+ * but the callback is never invoked.
+ */
+static void
+SSL_CTX_keylog_cb(const SSL *ssl, const char *line)
+{
+	int			fd;
+	mode_t		old_umask;
+	ssize_t		rc;
+	PGconn	   *conn = SSL_get_app_data(ssl);
 
 	if (conn == NULL)
 		return;
@@ -700,19 +709,24 @@ static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line) {
 	fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
 	umask(old_umask);
 
-	if (fd == -1) {
-		libpq_append_conn_error(conn, "could not open ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
+	if (fd == -1)
+	{
+		libpq_append_conn_error(conn, "could not open ssl key log file %s: %s",
+								conn->sslkeylogfile, pg_strerror(errno));
 		return;
 	}
 
-	bytes_written = dprintf(fd, "%s\n", line);
-	if (bytes_written < 0) {
-		libpq_append_conn_error(conn, "could not write to ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
-		close(fd);
-		return;
-	}
+	/* line is guaranteed by OpenSSL to be NUL terminated */
+	rc = write(fd, line, strlen(line));
+	if (rc < 0)
+		libpq_append_conn_error(conn, "could not write to ssl key log file %s: %s",
+								conn->sslkeylogfile, pg_strerror(errno));
+	else
+		rc = write(fd, "\n", 1);
+	(void) rc;					/* silence compiler warnings */
 	close(fd);
 }
+#endif
 
 /*
  *	Create per-connection SSL object, and load the client certificate,
@@ -1030,8 +1044,10 @@ initialize_SSL(PGconn *conn)
 	}
 	conn->ssl_in_use = true;
 
+#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
 	if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
 		SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
+#endif
 
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index ecf9bf7dd93..b1fc40b9e05 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -147,30 +147,27 @@ my $default_ssl_connstr =
 $common_connstr =
   "$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
 
-my $tempdir = PostgreSQL::Test::Utils::tempdir;
-
-# Connect should work with a given sslkeylogfile
-$node->connect_ok(
-        "$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/key.txt sslmode=require",
-        "connect with server root cert and sslkeylogfile=$tempdir/key.txt");
-
-# Verify the key file exists
-ok(-f "$tempdir/key.txt", "key log file exists");
-
-# Skip permission checks on Windows/Cygwin
 SKIP:
 {
-        skip "Permissions check not enforced on Windows", 1
-        if ($windows_os || $Config::Config{osname} eq 'cygwin');
+	skip "Keylogging is not supported with LibreSSL", 5 if $libressl;
+
+	my $tempdir = PostgreSQL::Test::Utils::tempdir;
+	my @status;
+
+	# Connect should work with a given sslkeylogfile
+	$node->connect_ok(
+		"$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/key.txt sslmode=require",
+		"connect with server root cert and sslkeylogfile=$tempdir/key.txt");
 
-        my $mode = (stat("$tempdir/key.txt"))[2];
-        my $permissions_ok = 1;
+	# Verify the key file exists
+	ok(-f "$tempdir/key.txt", "key log file exists");
 
-        if ($mode & 0006) {
-            $permissions_ok = 0;
-        }
+	# Skip permission checks on Windows/Cygwin
+	skip "Permissions check not enforced on Windows", 2
+	  if ($windows_os || $Config::Config{osname} eq 'cygwin');
 
-        ok($permissions_ok, "key log file is not world readble");
+	ok((@status = stat("$tempdir/key.txt")), "keylog file exists and returned status");
+	ok(@status && !($status[2] & 0006), "key log file is not world readable");
 }
 
 # The server should not accept non-SSL connections.
-- 
2.39.3 (Apple Git-146)

v8-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patchapplication/octet-stream; name=v8-0001-Add-support-for-dumping-SSL-keylog-to-a-file.patch; x-unix-mode=0644Download
From 9e72bd853ba971e526ebfdd7de986ca0969faa5d Mon Sep 17 00:00:00 2001
From: Abhishek Chanda <abhishek.becs@gmail.com>
Date: Tue, 7 Jan 2025 21:54:08 -0600
Subject: [PATCH v8 1/2] Add support for dumping SSL keylog to a file

This patch adds a new connection parameter which is used by libpq
to write keys used in a SSL context
---
 doc/src/sgml/installation.sgml           |  2 +-
 doc/src/sgml/libpq.sgml                  | 22 ++++++++++++++++
 src/interfaces/libpq/fe-connect.c        |  5 ++++
 src/interfaces/libpq/fe-secure-openssl.c | 33 ++++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h         |  1 +
 src/test/perl/PostgreSQL/Test/Utils.pm   |  1 +
 src/test/regress/pg_regress.c            |  1 +
 src/test/ssl/t/001_ssltests.pl           | 26 +++++++++++++++++++
 8 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml
index e076cefa3b9..39fd16651b9 100644
--- a/doc/src/sgml/installation.sgml
+++ b/doc/src/sgml/installation.sgml
@@ -301,7 +301,7 @@
       Additionally, <productname>LibreSSL</productname> is supported using the
       <productname>OpenSSL</productname> compatibility layer.  The minimum
       required version is 3.4 (from <systemitem class="osname">OpenBSD</systemitem>
-      version 7.0).
+      version 7.5).
      </para>
     </listitem>
 
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 8fa0515c6a0..31b711004aa 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1918,6 +1918,18 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
+      <term><literal>sslkeylogfile</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the location where libpq will log keys
+        used in this SSL context. This is useful for debugging postgres
+        protocol using tools like wireshark. This parameter is ignored if an
+        SSL connection is not made. Keys are logged in the NSS format.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslpassword" xreflabel="sslpassword">
       <term><literal>sslpassword</literal></term>
       <listitem>
@@ -9170,6 +9182,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGSSLKEYLOGFILE</envar></primary>
+      </indexterm>
+      <envar>PGSSLKEYLOGFILE</envar> behaves the same as the <xref
+      linkend="libpq-connect-sslkeylogfile"/> connection parameter.
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index d5051f5e820..180314ae73d 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -391,6 +391,11 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"OAuth-Scope", "", 15,
 	offsetof(struct pg_conn, oauth_scope)},
 
+	{"sslkeylogfile", "PGSSLKEYLOGFILE",
+		"", NULL,
+		"SSL-Key-Log-File", "", 0,	/* sizeof("") = 0 */
+	offsetof(struct pg_conn, sslkeylogfile)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 5bb9d9779d8..b0ca09b6700 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -57,6 +57,7 @@
  * include <wincrypt.h>, but some other Windows headers do.)
  */
 #include "common/openssl.h"
+#include <openssl/ssl.h>
 #include <openssl/conf.h>
 #ifdef USE_SSL_ENGINE
 #include <openssl/engine.h>
@@ -86,6 +87,7 @@ static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static PQsslKeyPassHook_OpenSSL_type PQsslKeyPassHook = NULL;
 static int	ssl_protocol_version_to_openssl(const char *protocol);
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line);
 
 /* ------------------------------------------------------------ */
 /*			 Procedures common to all secure sessions			*/
@@ -684,6 +686,34 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 /* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
 static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
 
+/* This is a callback that writes to a given ssl key log file */
+static void SSL_CTX_keylog_cb(const SSL *ssl, const char *line) {
+	int fd;
+	mode_t old_umask;
+	ssize_t bytes_written;
+	PGconn *conn = SSL_get_app_data(ssl);
+
+	if (conn == NULL)
+		return;
+
+	old_umask = umask(077);
+	fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
+	umask(old_umask);
+
+	if (fd == -1) {
+		libpq_append_conn_error(conn, "could not open ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
+		return;
+	}
+
+	bytes_written = dprintf(fd, "%s\n", line);
+	if (bytes_written < 0) {
+		libpq_append_conn_error(conn, "could not write to ssl key log file %s: %s", conn->sslkeylogfile, pg_strerror(errno));
+		close(fd);
+		return;
+	}
+	close(fd);
+}
+
 /*
  *	Create per-connection SSL object, and load the client certificate,
  *	private key, and trusted CA certs.
@@ -1000,6 +1030,9 @@ initialize_SSL(PGconn *conn)
 	}
 	conn->ssl_in_use = true;
 
+	if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
+		SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
+
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
 	 * as we have created the SSL object, and it will stick around for as long
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index f36f7f19d58..174b1315b86 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -432,6 +432,7 @@ struct pg_conn
 	char	   *load_balance_hosts; /* load balance over hosts */
 	char	   *scram_client_key;	/* base64-encoded SCRAM client key */
 	char	   *scram_server_key;	/* base64-encoded SCRAM server key */
+	char	   *sslkeylogfile;	/* where should the client write ssl key logs */
 
 	bool		cancelRequest;	/* true if this connection is used to send a
 								 * cancel request, instead of being a normal
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index efe0321a4ef..fedae5098aa 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -131,6 +131,7 @@ BEGIN
 	  PGSSLCRL
 	  PGSSLCRLDIR
 	  PGSSLKEY
+	  PGSSLKEYLOGFILE
 	  PGSSLMAXPROTOCOLVERSION
 	  PGSSLMINPROTOCOLVERSION
 	  PGSSLMODE
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 5d85dcc62f0..381a7266cb9 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -834,6 +834,7 @@ initialize_environment(void)
 		unsetenv("PGSSLCRL");
 		unsetenv("PGSSLCRLDIR");
 		unsetenv("PGSSLKEY");
+		unsetenv("PGSSLKEYLOGFILE");
 		unsetenv("PGSSLMAXPROTOCOLVERSION");
 		unsetenv("PGSSLMINPROTOCOLVERSION");
 		unsetenv("PGSSLMODE");
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 5422511d4ab..ecf9bf7dd93 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -147,6 +147,32 @@ my $default_ssl_connstr =
 $common_connstr =
   "$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
 
+my $tempdir = PostgreSQL::Test::Utils::tempdir;
+
+# Connect should work with a given sslkeylogfile
+$node->connect_ok(
+        "$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/key.txt sslmode=require",
+        "connect with server root cert and sslkeylogfile=$tempdir/key.txt");
+
+# Verify the key file exists
+ok(-f "$tempdir/key.txt", "key log file exists");
+
+# Skip permission checks on Windows/Cygwin
+SKIP:
+{
+        skip "Permissions check not enforced on Windows", 1
+        if ($windows_os || $Config::Config{osname} eq 'cygwin');
+
+        my $mode = (stat("$tempdir/key.txt"))[2];
+        my $permissions_ok = 1;
+
+        if ($mode & 0006) {
+            $permissions_ok = 0;
+        }
+
+        ok($permissions_ok, "key log file is not world readble");
+}
+
 # The server should not accept non-SSL connections.
 $node->connect_fails(
 	"$common_connstr sslmode=disable",
-- 
2.39.3 (Apple Git-146)

#14Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#13)
Re: Adding support for SSLKEYLOGFILE in the frontend

On Wed, Mar 5, 2025 at 9:21 AM Daniel Gustafsson <daniel@yesql.se> wrote:

I managed to misunderstand skip blocks in TAP tests in the 0002, so the
attached version fixes that. It has been failing on Debian in CI which I have
yet to look into.

Drive-by comment:

+    {"sslkeylogfile", "PGSSLKEYLOGFILE",
+        "", NULL,
+        "SSL-Key-Log-File", "", 0, /* sizeof("") = 0 */
+    offsetof(struct pg_conn, sslkeylogfile)},

Adding the PG prefix to the envvar name addresses my collision
concern, but I think Tom's comment upthread [1]/messages/by-id/1774813.1736385450@sss.pgh.pa.us was saying that we
should not provide any envvar at all:

I think it might be safer if we only accepted it as a connection
parameter and not via an environment variable.

Is the addition of the PG prefix enough to address that concern too?
(Are people already sanitizing their environments for all PG*
variables?)

Thanks,
--Jacob

[1]: /messages/by-id/1774813.1736385450@sss.pgh.pa.us

#15Tom Lane
tgl@sss.pgh.pa.us
In reply to: Jacob Champion (#14)
Re: Adding support for SSLKEYLOGFILE in the frontend

Jacob Champion <jacob.champion@enterprisedb.com> writes:

Adding the PG prefix to the envvar name addresses my collision
concern, but I think Tom's comment upthread [1] was saying that we
should not provide any envvar at all:

I think it might be safer if we only accepted it as a connection
parameter and not via an environment variable.

Is the addition of the PG prefix enough to address that concern too?

Indeed, I was advocating for *no* environment variable. The PG prefix
does not comfort me.

regards, tom lane

#16Daniel Gustafsson
daniel@yesql.se
In reply to: Tom Lane (#15)
1 attachment(s)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 13 Mar 2025, at 19:31, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Jacob Champion <jacob.champion@enterprisedb.com> writes:

Adding the PG prefix to the envvar name addresses my collision
concern, but I think Tom's comment upthread [1] was saying that we
should not provide any envvar at all:

I think it might be safer if we only accepted it as a connection
parameter and not via an environment variable.

Is the addition of the PG prefix enough to address that concern too?

Indeed, I was advocating for *no* environment variable. The PG prefix
does not comfort me.

Attached is a rebased version which fixes the test failure under autoconf (I
had missed git adding the configure file..) and Windows where the backslashes
weren't escaped properly. It also removes the environment variable and has
documentation touchups.

--
Daniel Gustafsson

Attachments:

v9-0001-libpq-Add-support-for-dumping-SSL-keylog-to-file.patchapplication/octet-stream; name=v9-0001-libpq-Add-support-for-dumping-SSL-keylog-to-file.patch; x-unix-mode=0644Download
From cad122ad984bc764315f2e2e94e06a25cf13b0b9 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Thu, 13 Mar 2025 22:55:35 +0100
Subject: [PATCH v9] 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 <abhishek.becs@gmail.com>
Co-authored-by: Daniel Gustafsson <daniel@yesql.se>
Reviewed-by: Jacob Champion <jacob.champion@enterprisedb.com>
Discussion: https://postgr.es/m/CAKiP-K85C8uQbzXKWf5wHQPkuygGUGcufke713iHmYWOe9q2dA@mail.gmail.com
---
 configure                                |  2 +-
 configure.ac                             |  2 +-
 doc/src/sgml/libpq.sgml                  | 23 +++++++++++
 meson.build                              |  1 +
 src/include/pg_config.h.in               |  3 ++
 src/interfaces/libpq/fe-connect.c        |  4 ++
 src/interfaces/libpq/fe-secure-openssl.c | 49 ++++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h         |  1 +
 src/test/ssl/t/001_ssltests.pl           | 27 +++++++++++++
 9 files changed, 110 insertions(+), 2 deletions(-)

diff --git a/configure b/configure
index 93fddd69981..e4cb7ede5ae 100755
--- a/configure
+++ b/configure
@@ -12796,7 +12796,7 @@ fi
 done
 
   # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
-  for ac_func in X509_get_signature_info SSL_CTX_set_num_tickets
+  for ac_func in X509_get_signature_info SSL_CTX_set_num_tickets SSL_CTX_set_keylog_callback
 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.ac b/configure.ac
index b6d02f5ecc7..c6f61f7e3a8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1370,7 +1370,7 @@ if test "$with_ssl" = openssl ; then
   # Function introduced in OpenSSL 1.0.2, not in LibreSSL.
   AC_CHECK_FUNCS([SSL_CTX_set_cert_cb])
   # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
-  AC_CHECK_FUNCS([X509_get_signature_info SSL_CTX_set_num_tickets])
+  AC_CHECK_FUNCS([X509_get_signature_info SSL_CTX_set_num_tickets SSL_CTX_set_keylog_callback])
   AC_DEFINE([USE_OPENSSL], 1, [Define to 1 to build with OpenSSL support. (--with-ssl=openssl)])
 elif test "$with_ssl" != no ; then
   AC_MSG_ERROR([--with-ssl must specify openssl])
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 8fa0515c6a0..d535cc2917b 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1918,6 +1918,29 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
+      <term><literal>sslkeylogfile</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the location where <literal>libpq</literal>
+        will log keys used in this SSL context.  This is useful for debugging
+        <productname>PostgreSQL</productname> protocol interations or client
+        connections using network inspection tools like
+        <productname>Wireshark</productname>.  This parameter is ignored if an
+        SSL connection is not made, or if <productname>LibreSSL</productname>
+        is used.  Keys are logged in the <productname>NSS</productname> format.
+
+        <warning>
+         <para>
+          Key logging will expose potentially sensitive information in the
+          keylog file and it should be handled with the same caution as
+          <xref linkend="libpq-connect-sslkey" /> files.
+         </para>
+        </warning>
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslpassword" xreflabel="sslpassword">
       <term><literal>sslpassword</literal></term>
       <listitem>
diff --git a/meson.build b/meson.build
index 13c13748e5d..07c51e7db58 100644
--- a/meson.build
+++ b/meson.build
@@ -1472,6 +1472,7 @@ if sslopt in ['auto', 'openssl']
       # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
       ['X509_get_signature_info'],
       ['SSL_CTX_set_num_tickets'],
+      ['SSL_CTX_set_keylog_callback'],
     ]
 
     are_openssl_funcs_complete = true
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index db6454090d2..64351a3adaa 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -361,6 +361,9 @@
 /* Define to 1 if you have the `SSL_CTX_set_ciphersuites' function. */
 #undef HAVE_SSL_CTX_SET_CIPHERSUITES
 
+/* Define to 1 if you have the `SSL_CTX_set_keylog_callback' function. */
+#undef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+
 /* Define to 1 if you have the `SSL_CTX_set_num_tickets' function. */
 #undef HAVE_SSL_CTX_SET_NUM_TICKETS
 
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index d5051f5e820..ead446b4eef 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -391,6 +391,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"OAuth-Scope", "", 15,
 	offsetof(struct pg_conn, oauth_scope)},
 
+	{"sslkeylogfile", NULL, NULL, NULL,
+		"SSL-Key-Log-File", "", 0,	/* sizeof("") = 0 */
+	offsetof(struct pg_conn, sslkeylogfile)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 5bb9d9779d8..b6476b22ae9 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -57,6 +57,7 @@
  * include <wincrypt.h>, but some other Windows headers do.)
  */
 #include "common/openssl.h"
+#include <openssl/ssl.h>
 #include <openssl/conf.h>
 #ifdef USE_SSL_ENGINE
 #include <openssl/engine.h>
@@ -684,6 +685,49 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 /* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
 static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
 
+#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+/*
+ * SSL Key Logging callback
+ *
+ * This callback lets the user store all key material to a file for debugging
+ * purposes.  The file will be written using the NSS keylog format.  LibreSSL
+ * 3.5 introduce stub function to set the callback for OpenSSL compatibility,
+ * but the callback is never invoked.
+ */
+static void
+SSL_CTX_keylog_cb(const SSL *ssl, const char *line)
+{
+	int			fd;
+	mode_t		old_umask;
+	ssize_t		rc;
+	PGconn	   *conn = SSL_get_app_data(ssl);
+
+	if (conn == NULL)
+		return;
+
+	old_umask = umask(077);
+	fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
+	umask(old_umask);
+
+	if (fd == -1)
+	{
+		libpq_append_conn_error(conn, "could not open ssl keylog file %s: %s",
+								conn->sslkeylogfile, pg_strerror(errno));
+		return;
+	}
+
+	/* line is guaranteed by OpenSSL to be NUL terminated */
+	rc = write(fd, line, strlen(line));
+	if (rc < 0)
+		libpq_append_conn_error(conn, "could not write to ssl keylog file %s: %s",
+								conn->sslkeylogfile, pg_strerror(errno));
+	else
+		rc = write(fd, "\n", 1);
+	(void) rc;					/* silence compiler warnings */
+	close(fd);
+}
+#endif
+
 /*
  *	Create per-connection SSL object, and load the client certificate,
  *	private key, and trusted CA certs.
@@ -1000,6 +1044,11 @@ initialize_SSL(PGconn *conn)
 	}
 	conn->ssl_in_use = true;
 
+#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+	if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
+		SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
+#endif
+
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
 	 * as we have created the SSL object, and it will stick around for as long
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index f36f7f19d58..cd15028ade9 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -432,6 +432,7 @@ struct pg_conn
 	char	   *load_balance_hosts; /* load balance over hosts */
 	char	   *scram_client_key;	/* base64-encoded SCRAM client key */
 	char	   *scram_server_key;	/* base64-encoded SCRAM server key */
+	char	   *sslkeylogfile;	/* where should the client write ssl keylogs */
 
 	bool		cancelRequest;	/* true if this connection is used to send a
 								 * cancel request, instead of being a normal
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 5422511d4ab..086abf3b8b3 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -147,6 +147,33 @@ my $default_ssl_connstr =
 $common_connstr =
   "$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
 
+SKIP:
+{
+	skip "Keylogging is not supported with LibreSSL", 5 if $libressl;
+
+	my $tempdir = PostgreSQL::Test::Utils::tempdir;
+	my @status;
+
+	# Properly escape backslashes in the path
+	$tempdir =~ s/\\/\\\\/g;
+
+	# Connect should work with a given sslkeylogfile
+	$node->connect_ok(
+		"$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/key.txt sslmode=require",
+		"connect with server root cert and sslkeylogfile=$tempdir/key.txt");
+
+	# Verify the key file exists
+	ok(-f "$tempdir/key.txt", "keylog file exists at: $tempdir/key.txt");
+
+	# Skip permission checks on Windows/Cygwin
+	skip "Permissions check not enforced on Windows", 2
+	  if ($windows_os || $Config::Config{osname} eq 'cygwin');
+
+	ok( (@status = stat("$tempdir/key.txt")),
+		"keylog file exists and returned status");
+	ok(@status && !($status[2] & 0006), "keylog file is not world readable");
+}
+
 # The server should not accept non-SSL connections.
 $node->connect_fails(
 	"$common_connstr sslmode=disable",
-- 
2.39.3 (Apple Git-146)

#17Abhishek Chanda
abhishek.becs@gmail.com
In reply to: Daniel Gustafsson (#16)
Re: Adding support for SSLKEYLOGFILE in the frontend

Thanks, Daniel.

Should there be the ifdef guard in here as well?

+ {"sslkeylogfile", NULL, NULL, NULL,
+ "SSL-Key-Log-File", "", 0, /* sizeof("") = 0 */
+ offsetof(struct pg_conn, sslkeylogfile)},
+

A small nit, this line should say NULL

+ /* line is guaranteed by OpenSSL to be NUL terminated */

Thanks

On Thu, Mar 13, 2025 at 5:07 PM Daniel Gustafsson <daniel@yesql.se> wrote:

On 13 Mar 2025, at 19:31, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Jacob Champion <jacob.champion@enterprisedb.com> writes:

Adding the PG prefix to the envvar name addresses my collision
concern, but I think Tom's comment upthread [1] was saying that we
should not provide any envvar at all:

I think it might be safer if we only accepted it as a connection
parameter and not via an environment variable.

Is the addition of the PG prefix enough to address that concern too?

Indeed, I was advocating for *no* environment variable. The PG prefix
does not comfort me.

Attached is a rebased version which fixes the test failure under autoconf (I
had missed git adding the configure file..) and Windows where the backslashes
weren't escaped properly. It also removes the environment variable and has
documentation touchups.

--
Daniel Gustafsson

--
Thanks and regards
Abhishek Chanda

#18Daniel Gustafsson
daniel@yesql.se
In reply to: Abhishek Chanda (#17)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 14 Mar 2025, at 00:02, Abhishek Chanda <abhishek.becs@gmail.com> wrote:

Thanks, Daniel.

Should there be the ifdef guard in here as well?

+ {"sslkeylogfile", NULL, NULL, NULL,
+ "SSL-Key-Log-File", "", 0, /* sizeof("") = 0 */
+ offsetof(struct pg_conn, sslkeylogfile)},

No, we want the option to work even if the feature doesn't in order to not make
connection strings dependent on compilation options.

A small nit, this line should say NULL

+ /* line is guaranteed by OpenSSL to be NUL terminated */

The variable is terminated by the NUL character so I believe this is actually
correct, if you look around in the source tree you'll find many more
references.

On Thu, Mar 13, 2025 at 5:07 PM Daniel Gustafsson <daniel@yesql.se> wrote:

On 13 Mar 2025, at 19:31, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Jacob Champion <jacob.champion@enterprisedb.com> writes:

Adding the PG prefix to the envvar name addresses my collision
concern, but I think Tom's comment upthread [1] was saying that we
should not provide any envvar at all:

I think it might be safer if we only accepted it as a connection
parameter and not via an environment variable.

Is the addition of the PG prefix enough to address that concern too?

Indeed, I was advocating for *no* environment variable. The PG prefix
does not comfort me.

Attached is a rebased version which fixes the test failure under autoconf (I
had missed git adding the configure file..) and Windows where the backslashes
weren't escaped properly. It also removes the environment variable and has
documentation touchups.

--
Daniel Gustafsson

#19Peter Eisentraut
peter@eisentraut.org
In reply to: Tom Lane (#15)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 13.03.25 19:31, Tom Lane wrote:

Jacob Champion <jacob.champion@enterprisedb.com> writes:

Adding the PG prefix to the envvar name addresses my collision
concern, but I think Tom's comment upthread [1] was saying that we
should not provide any envvar at all:

I think it might be safer if we only accepted it as a connection
parameter and not via an environment variable.

Is the addition of the PG prefix enough to address that concern too?

Indeed, I was advocating for *no* environment variable. The PG prefix
does not comfort me.

It seems to me that the environment variable would be the most useful
way to use this feature, for example if you want to debug an existing
program that you can't or don't want to change.

As was mentioned earlier, libcurl uses an environment variable for this.
Moreover, the format originated in the NSS library, which also uses an
environment variable.

So we are here constructing a higher level of security that others don't
seem to have found the need for.

It's also possible that we should consider the SSLKEYLOGFILE environment
variable some kind of quasi-standard like PAGER, and we should be using
exactly that environment variable name like everyone else.

#20vignesh C
vignesh21@gmail.com
In reply to: Daniel Gustafsson (#16)
Re: Adding support for SSLKEYLOGFILE in the frontend

On Fri, 14 Mar 2025 at 03:38, Daniel Gustafsson <daniel@yesql.se> wrote:

On 13 Mar 2025, at 19:31, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Jacob Champion <jacob.champion@enterprisedb.com> writes:

Adding the PG prefix to the envvar name addresses my collision
concern, but I think Tom's comment upthread [1] was saying that we
should not provide any envvar at all:

I think it might be safer if we only accepted it as a connection
parameter and not via an environment variable.

Is the addition of the PG prefix enough to address that concern too?

Indeed, I was advocating for *no* environment variable. The PG prefix
does not comfort me.

Attached is a rebased version which fixes the test failure under autoconf (I
had missed git adding the configure file..) and Windows where the backslashes
weren't escaped properly. It also removes the environment variable and has
documentation touchups.

I noticed that Peter's comments from [1]/messages/by-id/68b66b6d-cc59-44f8-bdd2-248d50055740@eisentraut.org is not yet addressed. I have
changed the commitfest entry status to Waiting on Author, please
address them and update the status to Needs review.
[1]: /messages/by-id/68b66b6d-cc59-44f8-bdd2-248d50055740@eisentraut.org

Regards.
Vignesh

#21Daniel Gustafsson
daniel@yesql.se
In reply to: Peter Eisentraut (#19)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 14 Mar 2025, at 15:27, Peter Eisentraut <peter@eisentraut.org> wrote:

On 13.03.25 19:31, Tom Lane wrote:

Jacob Champion <jacob.champion@enterprisedb.com> writes:

Adding the PG prefix to the envvar name addresses my collision
concern, but I think Tom's comment upthread [1] was saying that we
should not provide any envvar at all:

I think it might be safer if we only accepted it as a connection
parameter and not via an environment variable.

Is the addition of the PG prefix enough to address that concern too?

Indeed, I was advocating for *no* environment variable. The PG prefix
does not comfort me.

It seems to me that the environment variable would be the most useful way to use this feature, for example if you want to debug an existing program that you can't or don't want to change.

As was mentioned earlier, libcurl uses an environment variable for this. Moreover, the format originated in the NSS library, which also uses an environment variable.

So we are here constructing a higher level of security that others don't seem to have found the need for.

IIRC the reasoning has been that if a rogue user can inject an environment
variable into your session and read your files it's probably game over anyways.

It's also possible that we should consider the SSLKEYLOGFILE environment variable some kind of quasi-standard like PAGER, and we should be using exactly that environment variable name like everyone else.

If we would use the same as others, it would make it harder to do fine-grained
debugging of a session

--
Daniel Gustafsson

#22Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#21)
Re: Adding support for SSLKEYLOGFILE in the frontend

On Sun, Mar 16, 2025 at 6:49 AM Daniel Gustafsson <daniel@yesql.se> wrote:

IIRC the reasoning has been that if a rogue user can inject an environment
variable into your session and read your files it's probably game over anyways.

(Personally I'm no longer as convinced by this line of argument as I
once was...)

It's also possible that we should consider the SSLKEYLOGFILE environment variable some kind of quasi-standard like PAGER, and we should be using exactly that environment variable name like everyone else.

If we would use the same as others, it would make it harder to do fine-grained
debugging of a session

It also brings up the possibility of two (or more?) separate parts of
the client writing keys simultaneously to the same file through
separate file descriptors, which doesn't seem very fun.

--Jacob

#23Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#22)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 17 Mar 2025, at 16:48, Jacob Champion <jacob.champion@enterprisedb.com> wrote:

On Sun, Mar 16, 2025 at 6:49 AM Daniel Gustafsson <daniel@yesql.se> wrote:

IIRC the reasoning has been that if a rogue user can inject an environment
variable into your session and read your files it's probably game over anyways.

(Personally I'm no longer as convinced by this line of argument as I
once was...)

Since there is disagreement over this, we should either 1) go ahead with the
latest patch without an env var and revisit the discussion during v19; 2)
adding the env var back into the patch as PGSSLKEYLOGFILE or; 3) postponing all
of this till v19?

Personally I think this feature has enough value even without the env var to
not postpone it, especially since adding an env var in 19 will still be
backwards compatible. I would go for option 1 to stay on the safe side and
allow time for proper discussion, any other thoughts?

--
Daniel Gustafsson

#24Jelte Fennema-Nio
postgres@jeltef.nl
In reply to: Daniel Gustafsson (#23)
Re: Adding support for SSLKEYLOGFILE in the frontend

On Tue, 18 Mar 2025 at 13:43, Daniel Gustafsson <daniel@yesql.se> wrote:

Since there is disagreement over this, we should either 1) go ahead with the
latest patch without an env var and revisit the discussion during v19; 2)
adding the env var back into the patch as PGSSLKEYLOGFILE or; 3) postponing all
of this till v19?

Let's do 1.

nit about the actual code: let's put the new option next to the other
ssl options in PQconninfoOptions instead of all the way at the end.

#25Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#23)
Re: Adding support for SSLKEYLOGFILE in the frontend

On Tue, Mar 18, 2025 at 5:43 AM Daniel Gustafsson <daniel@yesql.se> wrote:

Personally I think this feature has enough value even without the env var to
not postpone it, especially since adding an env var in 19 will still be
backwards compatible. I would go for option 1 to stay on the safe side and
allow time for proper discussion, any other thoughts?

+1

--Jacob

#26Álvaro Herrera
alvherre@alvh.no-ip.org
In reply to: Daniel Gustafsson (#16)
Re: Adding support for SSLKEYLOGFILE in the frontend

Hello,

It seems there's rough consensus on proceeding with a connection param
and no environment variable. TBH it's not very clear to me that an
envvar is a great way to drive this, even if there weren't security
considerations at play, just considering the case of a multithreaded
program that opens two connections ... reading that log file is going to
be super fun.

In initialize_SSL(), the test for conn->sslkeylogfile is inside the
#ifdef for the existance of the SSL function. I think it's better to
log a message (probably just a warning) that says "this feature is not
supported with this TLS library" rather than doing nothing. Silently
failing to act is just painful for the user who then has to go to our
source file to figure out why the setting isn't taking effect.

Thanks,

--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
"La primera ley de las demostraciones en vivo es: no trate de usar el sistema.
Escriba un guión que no toque nada para no causar daños." (Jakob Nielsen)

#27Heikki Linnakangas
hlinnaka@iki.fi
In reply to: Álvaro Herrera (#26)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 20/03/2025 11:39, Álvaro Herrera wrote:

Hello,

It seems there's rough consensus on proceeding with a connection param
and no environment variable. TBH it's not very clear to me that an
envvar is a great way to drive this, even if there weren't security
considerations at play, just considering the case of a multithreaded
program that opens two connections ... reading that log file is going to
be super fun.

I believe the usual way to use SSLKEYLOGFILE is indeed to append all
keys to the same file. That's how I use, at least. I'm not sure if
openssl has some locking on it, but I've never had a problem with having
data from different connections mixed up. The lines are not that long,
it probably just relies on write(2) being atomic enough.

--
Heikki Linnakangas
Neon (https://neon.tech)

#28Jelte Fennema-Nio
postgres@jeltef.nl
In reply to: Jacob Champion (#22)
Re: Adding support for SSLKEYLOGFILE in the frontend

On Mon, 17 Mar 2025 at 16:48, Jacob Champion
<jacob.champion@enterprisedb.com> wrote:

On Sun, Mar 16, 2025 at 6:49 AM Daniel Gustafsson <daniel@yesql.se> wrote:

IIRC the reasoning has been that if a rogue user can inject an environment
variable into your session and read your files it's probably game over anyways.

(Personally I'm no longer as convinced by this line of argument as I
once was...)

I'm not saying there's no attack possible here (although I cannot
think of one), but we allow configuring every other SSL option using
an env var^1. So if there is an attack possible, why would that only
apply to being able to control the sslkeylogfile as opposed to e.g.
sslmode or sslrootcert.

^1 except for "sslpassword", which is weird because that seems exactly
like one of the options you might not want to store in a connection
string for security reasons.

#29Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Heikki Linnakangas (#27)
Re: Adding support for SSLKEYLOGFILE in the frontend

On Thu, Mar 20, 2025 at 3:58 AM Heikki Linnakangas <hlinnaka@iki.fi> wrote:

I'm not sure if openssl has some locking on it,

OpenSSL leaves it up to the application (us). OpenSSL 3.5 will
apparently add a builtin implementation, which from a quick skim does
use locking. As another datapoint, libcurl's implementation appears to
rely on implicit flockfile().

--Jacob

#30Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Jelte Fennema-Nio (#28)
Re: Adding support for SSLKEYLOGFILE in the frontend

On Thu, Mar 20, 2025 at 6:11 AM Jelte Fennema-Nio <postgres@jeltef.nl> wrote:

I'm not saying there's no attack possible here (although I cannot
think of one), but we allow configuring every other SSL option using
an env var^1. So if there is an attack possible, why would that only
apply to being able to control the sslkeylogfile as opposed to e.g.
sslmode or sslrootcert.

(First off: I was specifically referring to the "if X and Y, then it's
game over, so might as well Z too" line of argument. I think that kind
of reasoning causes problems in OSS security.)

I don't think the argument against an envvar is being made from the
point of view of consistency. That's not very helpful in security
contexts, where we'd rather be inconsistently good/better than
consistently bad. (And existing clients that allow an adversary to
control the PGSSLROOTCERT environment variable are broken, and have
been since, what, 2009? I'm not going to spend energy on that.)

The argument is about net-new behavior: is it okay for a new
environment variable to silently log key material for all our
connections to an arbitrary location on disk? I feel less strongly
about it than Tom does, looks like, but the additional risk is
definitely not zero. Maybe we could declare a prefix of environment
variables to be security-critical or something, to help that concern
in the future.

OpenSSL 3.5 will add native support for the SSLKEYLOGFILE envvar, but
I think they've locked it behind both a build-time option and a
default-off configuration setting. And there are moving discussions
[1]: https://github.com/openssl/openssl/issues/26282
logging callback used in this patch, too. I think Tom's concern has
merit, and I think we should move cautiously here.

Thanks,
--Jacob

[1]: https://github.com/openssl/openssl/issues/26282

#31Daniel Gustafsson
daniel@yesql.se
In reply to: Álvaro Herrera (#26)
1 attachment(s)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 20 Mar 2025, at 10:39, Álvaro Herrera <alvherre@alvh.no-ip.org> wrote:

In initialize_SSL(), the test for conn->sslkeylogfile is inside the
#ifdef for the existance of the SSL function. I think it's better to
log a message (probably just a warning) that says "this feature is not
supported with this TLS library" rather than doing nothing. Silently
failing to act is just painful for the user who then has to go to our
source file to figure out why the setting isn't taking effect.

The only cases when the function isn't defined are the two oldest LibreSSL
versions we support, but even with a LibreSSL version that does have the
function the code is dead since LibreSSL only implements stubs for OpenSSL
compatibility. This is documented in our docs, but we might as well help the
user further by logging a warning as you suggest. The attached v10 adds a
version for the two cases when key logging won't happen (in reality it will be
just one case for LibreSSL but with this we can handle a purpose built OpenSSL
without the callback).

--
Daniel Gustafsson

Attachments:

v10-0001-libpq-Add-support-for-dumping-SSL-keylog-to-file.patchapplication/octet-stream; name=v10-0001-libpq-Add-support-for-dumping-SSL-keylog-to-file.patch; x-unix-mode=0644Download
From 22c5ff0793ecb770124d7843aaf13c289f974d7e Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Thu, 13 Mar 2025 22:55:35 +0100
Subject: [PATCH v10] 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 <abhishek.becs@gmail.com>
Co-authored-by: Daniel Gustafsson <daniel@yesql.se>
Reviewed-by: Jacob Champion <jacob.champion@enterprisedb.com>
Discussion: https://postgr.es/m/CAKiP-K85C8uQbzXKWf5wHQPkuygGUGcufke713iHmYWOe9q2dA@mail.gmail.com
---
 configure                                |  2 +-
 configure.ac                             |  2 +-
 doc/src/sgml/libpq.sgml                  | 23 ++++++++++
 meson.build                              |  1 +
 src/include/pg_config.h.in               |  3 ++
 src/interfaces/libpq/fe-connect.c        |  4 ++
 src/interfaces/libpq/fe-secure-openssl.c | 58 ++++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h         |  1 +
 src/test/ssl/t/001_ssltests.pl           | 27 +++++++++++
 9 files changed, 119 insertions(+), 2 deletions(-)

diff --git a/configure b/configure
index fac1e9a4e39..1d9e324780b 100755
--- a/configure
+++ b/configure
@@ -12792,7 +12792,7 @@ fi
 done
 
   # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
-  for ac_func in X509_get_signature_info SSL_CTX_set_num_tickets
+  for ac_func in X509_get_signature_info SSL_CTX_set_num_tickets SSL_CTX_set_keylog_callback
 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.ac b/configure.ac
index b6d02f5ecc7..c6f61f7e3a8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1370,7 +1370,7 @@ if test "$with_ssl" = openssl ; then
   # Function introduced in OpenSSL 1.0.2, not in LibreSSL.
   AC_CHECK_FUNCS([SSL_CTX_set_cert_cb])
   # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
-  AC_CHECK_FUNCS([X509_get_signature_info SSL_CTX_set_num_tickets])
+  AC_CHECK_FUNCS([X509_get_signature_info SSL_CTX_set_num_tickets SSL_CTX_set_keylog_callback])
   AC_DEFINE([USE_OPENSSL], 1, [Define to 1 to build with OpenSSL support. (--with-ssl=openssl)])
 elif test "$with_ssl" != no ; then
   AC_MSG_ERROR([--with-ssl must specify openssl])
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index b359fbff295..610aa0ff5bf 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1918,6 +1918,29 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
+      <term><literal>sslkeylogfile</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the location where <literal>libpq</literal>
+        will log keys used in this SSL context.  This is useful for debugging
+        <productname>PostgreSQL</productname> protocol interations or client
+        connections using network inspection tools like
+        <productname>Wireshark</productname>.  This parameter is ignored if an
+        SSL connection is not made, or if <productname>LibreSSL</productname>
+        is used.  Keys are logged in the <productname>NSS</productname> format.
+
+        <warning>
+         <para>
+          Key logging will expose potentially sensitive information in the
+          keylog file and it should be handled with the same caution as
+          <xref linkend="libpq-connect-sslkey" /> files.
+         </para>
+        </warning>
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslpassword" xreflabel="sslpassword">
       <term><literal>sslpassword</literal></term>
       <listitem>
diff --git a/meson.build b/meson.build
index 7cf518a2765..312c11a82d0 100644
--- a/meson.build
+++ b/meson.build
@@ -1467,6 +1467,7 @@ if sslopt in ['auto', 'openssl']
       # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
       ['X509_get_signature_info'],
       ['SSL_CTX_set_num_tickets'],
+      ['SSL_CTX_set_keylog_callback'],
     ]
 
     are_openssl_funcs_complete = true
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index db6454090d2..64351a3adaa 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -361,6 +361,9 @@
 /* Define to 1 if you have the `SSL_CTX_set_ciphersuites' function. */
 #undef HAVE_SSL_CTX_SET_CIPHERSUITES
 
+/* Define to 1 if you have the `SSL_CTX_set_keylog_callback' function. */
+#undef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+
 /* Define to 1 if you have the `SSL_CTX_set_num_tickets' function. */
 #undef HAVE_SSL_CTX_SET_NUM_TICKETS
 
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index d5051f5e820..ead446b4eef 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -391,6 +391,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"OAuth-Scope", "", 15,
 	offsetof(struct pg_conn, oauth_scope)},
 
+	{"sslkeylogfile", NULL, NULL, NULL,
+		"SSL-Key-Log-File", "", 0,	/* sizeof("") = 0 */
+	offsetof(struct pg_conn, sslkeylogfile)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 5bb9d9779d8..e74ec4ebebb 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -57,6 +57,7 @@
  * include <wincrypt.h>, but some other Windows headers do.)
  */
 #include "common/openssl.h"
+#include <openssl/ssl.h>
 #include <openssl/conf.h>
 #ifdef USE_SSL_ENGINE
 #include <openssl/engine.h>
@@ -684,6 +685,49 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 /* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
 static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
 
+#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+/*
+ * SSL Key Logging callback
+ *
+ * This callback lets the user store all key material to a file for debugging
+ * purposes.  The file will be written using the NSS keylog format.  LibreSSL
+ * 3.5 introduce stub function to set the callback for OpenSSL compatibility,
+ * but the callback is never invoked.
+ */
+static void
+SSL_CTX_keylog_cb(const SSL *ssl, const char *line)
+{
+	int			fd;
+	mode_t		old_umask;
+	ssize_t		rc;
+	PGconn	   *conn = SSL_get_app_data(ssl);
+
+	if (conn == NULL)
+		return;
+
+	old_umask = umask(077);
+	fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
+	umask(old_umask);
+
+	if (fd == -1)
+	{
+		libpq_append_conn_error(conn, "could not open ssl keylog file %s: %s",
+								conn->sslkeylogfile, pg_strerror(errno));
+		return;
+	}
+
+	/* line is guaranteed by OpenSSL to be NUL terminated */
+	rc = write(fd, line, strlen(line));
+	if (rc < 0)
+		libpq_append_conn_error(conn, "could not write to ssl keylog file %s: %s",
+								conn->sslkeylogfile, pg_strerror(errno));
+	else
+		rc = write(fd, "\n", 1);
+	(void) rc;					/* silence compiler warnings */
+	close(fd);
+}
+#endif
+
 /*
  *	Create per-connection SSL object, and load the client certificate,
  *	private key, and trusted CA certs.
@@ -1000,6 +1044,20 @@ initialize_SSL(PGconn *conn)
 	}
 	conn->ssl_in_use = true;
 
+	if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
+	{
+#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+		SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
+#else
+#ifdef LIBRESSL_VERSION_NUMBER
+		fprintf(stderr, libpq_gettext("WARNING: sslkeylog support requires OpenSSL\n"));
+#else
+		fprintf(stderr, libpq_gettext("WARNING: libpq was not built with sslkeylog support\n"));
+#endif
+#endif
+	}
+
+
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
 	 * as we have created the SSL object, and it will stick around for as long
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index ade5ad82f07..ac148fe8c32 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -424,6 +424,7 @@ struct pg_conn
 	char	   *load_balance_hosts; /* load balance over hosts */
 	char	   *scram_client_key;	/* base64-encoded SCRAM client key */
 	char	   *scram_server_key;	/* base64-encoded SCRAM server key */
+	char	   *sslkeylogfile;	/* where should the client write ssl keylogs */
 
 	bool		cancelRequest;	/* true if this connection is used to send a
 								 * cancel request, instead of being a normal
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 5422511d4ab..086abf3b8b3 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -147,6 +147,33 @@ my $default_ssl_connstr =
 $common_connstr =
   "$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
 
+SKIP:
+{
+	skip "Keylogging is not supported with LibreSSL", 5 if $libressl;
+
+	my $tempdir = PostgreSQL::Test::Utils::tempdir;
+	my @status;
+
+	# Properly escape backslashes in the path
+	$tempdir =~ s/\\/\\\\/g;
+
+	# Connect should work with a given sslkeylogfile
+	$node->connect_ok(
+		"$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/key.txt sslmode=require",
+		"connect with server root cert and sslkeylogfile=$tempdir/key.txt");
+
+	# Verify the key file exists
+	ok(-f "$tempdir/key.txt", "keylog file exists at: $tempdir/key.txt");
+
+	# Skip permission checks on Windows/Cygwin
+	skip "Permissions check not enforced on Windows", 2
+	  if ($windows_os || $Config::Config{osname} eq 'cygwin');
+
+	ok( (@status = stat("$tempdir/key.txt")),
+		"keylog file exists and returned status");
+	ok(@status && !($status[2] & 0006), "keylog file is not world readable");
+}
+
 # The server should not accept non-SSL connections.
 $node->connect_fails(
 	"$common_connstr sslmode=disable",
-- 
2.39.3 (Apple Git-146)

#32Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Gustafsson (#31)
1 attachment(s)
Re: Adding support for SSLKEYLOGFILE in the frontend

I took another pass at this one and did a few small tweaks, so I would to take
it for another spin across CI before looking at committing it. There are no
functionality changes, only polish.

--
Daniel Gustafsson

Attachments:

v11-0001-libpq-Add-support-for-dumping-SSL-keylog-to-file.patchapplication/octet-stream; name=v11-0001-libpq-Add-support-for-dumping-SSL-keylog-to-file.patch; x-unix-mode=0644Download
From 6f5af1cd5dd3ab6047f4781bdc79d4053022cad9 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Thu, 13 Mar 2025 22:55:35 +0100
Subject: [PATCH v11] 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 <abhishek.becs@gmail.com>
Co-authored-by: Daniel Gustafsson <daniel@yesql.se>
Reviewed-by: Jacob Champion <jacob.champion@enterprisedb.com>
Discussion: https://postgr.es/m/CAKiP-K85C8uQbzXKWf5wHQPkuygGUGcufke713iHmYWOe9q2dA@mail.gmail.com
---
 configure                                |  2 +-
 configure.ac                             |  2 +-
 doc/src/sgml/libpq.sgml                  | 24 ++++++++++
 meson.build                              |  1 +
 src/include/pg_config.h.in               |  3 ++
 src/interfaces/libpq/fe-connect.c        |  4 ++
 src/interfaces/libpq/fe-secure-openssl.c | 58 ++++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h         |  1 +
 src/test/ssl/t/001_ssltests.pl           | 27 +++++++++++
 9 files changed, 120 insertions(+), 2 deletions(-)

diff --git a/configure b/configure
index 30d949c3c46..ac343659c24 100755
--- a/configure
+++ b/configure
@@ -12931,7 +12931,7 @@ fi
 done
 
   # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
-  for ac_func in X509_get_signature_info SSL_CTX_set_num_tickets
+  for ac_func in X509_get_signature_info SSL_CTX_set_num_tickets SSL_CTX_set_keylog_callback
 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.ac b/configure.ac
index 25cdfcf65af..1f48ed3f116 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1382,7 +1382,7 @@ if test "$with_ssl" = openssl ; then
   # Function introduced in OpenSSL 1.0.2, not in LibreSSL.
   AC_CHECK_FUNCS([SSL_CTX_set_cert_cb])
   # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
-  AC_CHECK_FUNCS([X509_get_signature_info SSL_CTX_set_num_tickets])
+  AC_CHECK_FUNCS([X509_get_signature_info SSL_CTX_set_num_tickets SSL_CTX_set_keylog_callback])
   AC_DEFINE([USE_OPENSSL], 1, [Define to 1 to build with OpenSSL support. (--with-ssl=openssl)])
 elif test "$with_ssl" != no ; then
   AC_MSG_ERROR([--with-ssl must specify openssl])
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index b359fbff295..c49605c4879 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1918,6 +1918,30 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
       </listitem>
      </varlistentry>
 
+     <varlistentry id="libpq-connect-sslkeylogfile" xreflabel="sslkeylogfile">
+      <term><literal>sslkeylogfile</literal></term>
+      <listitem>
+       <para>
+        This parameter specifies the location where <literal>libpq</literal>
+        will log keys used in this SSL context.  This is useful for debugging
+        <productname>PostgreSQL</productname> protocol interactions or client
+        connections using network inspection tools like
+        <productname>Wireshark</productname>.  This parameter is ignored if an
+        SSL connection is not made, or if <productname>LibreSSL</productname>
+        is used (<productname>LibreSSL</productname> does not support key
+        logging).  Keys are logged using the <productname>NSS</productname>
+        format.
+        <warning>
+         <para>
+          Key logging will expose potentially sensitive information in the
+          keylog file.  Keylog files should be handled with the same care as
+          <xref linkend="libpq-connect-sslkey" /> files.
+         </para>
+        </warning>
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="libpq-connect-sslpassword" xreflabel="sslpassword">
       <term><literal>sslpassword</literal></term>
       <listitem>
diff --git a/meson.build b/meson.build
index b8da4966297..7d5e3348a73 100644
--- a/meson.build
+++ b/meson.build
@@ -1479,6 +1479,7 @@ if sslopt in ['auto', 'openssl']
       # Function introduced in OpenSSL 1.1.1, not in LibreSSL.
       ['X509_get_signature_info'],
       ['SSL_CTX_set_num_tickets'],
+      ['SSL_CTX_set_keylog_callback'],
     ]
 
     are_openssl_funcs_complete = true
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 92f0616c400..8e35c02e208 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -364,6 +364,9 @@
 /* Define to 1 if you have the `SSL_CTX_set_ciphersuites' function. */
 #undef HAVE_SSL_CTX_SET_CIPHERSUITES
 
+/* Define to 1 if you have the `SSL_CTX_set_keylog_callback' function. */
+#undef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+
 /* Define to 1 if you have the `SSL_CTX_set_num_tickets' function. */
 #undef HAVE_SSL_CTX_SET_NUM_TICKETS
 
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index d5051f5e820..ead446b4eef 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -391,6 +391,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
 		"OAuth-Scope", "", 15,
 	offsetof(struct pg_conn, oauth_scope)},
 
+	{"sslkeylogfile", NULL, NULL, NULL,
+		"SSL-Key-Log-File", "", 0,	/* sizeof("") = 0 */
+	offsetof(struct pg_conn, sslkeylogfile)},
+
 	/* Terminating entry --- MUST BE LAST */
 	{NULL, NULL, NULL, NULL,
 	NULL, NULL, 0}
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 5bb9d9779d8..4bfd8e0447c 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -57,6 +57,7 @@
  * include <wincrypt.h>, but some other Windows headers do.)
  */
 #include "common/openssl.h"
+#include <openssl/ssl.h>
 #include <openssl/conf.h>
 #ifdef USE_SSL_ENGINE
 #include <openssl/engine.h>
@@ -684,6 +685,49 @@ pgtls_verify_peer_name_matches_certificate_guts(PGconn *conn,
 /* See pqcomm.h comments on OpenSSL implementation of ALPN (RFC 7301) */
 static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
 
+#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+/*
+ * SSL Key Logging callback
+ *
+ * This callback lets the user store all key material to a file for debugging
+ * purposes.  The file will be written using the NSS keylog format.  LibreSSL
+ * 3.5 introduced stub function to set the callback for OpenSSL compatibility
+ * but the callback is never invoked.
+ */
+static void
+SSL_CTX_keylog_cb(const SSL *ssl, const char *line)
+{
+	int			fd;
+	mode_t		old_umask;
+	ssize_t		rc;
+	PGconn	   *conn = SSL_get_app_data(ssl);
+
+	if (conn == NULL)
+		return;
+
+	old_umask = umask(077);
+	fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
+	umask(old_umask);
+
+	if (fd == -1)
+	{
+		libpq_append_conn_error(conn, "could not open ssl keylog file %s: %s",
+								conn->sslkeylogfile, pg_strerror(errno));
+		return;
+	}
+
+	/* line is guaranteed by OpenSSL to be NUL terminated */
+	rc = write(fd, line, strlen(line));
+	if (rc < 0)
+		libpq_append_conn_error(conn, "could not write to ssl keylog file %s: %s",
+								conn->sslkeylogfile, pg_strerror(errno));
+	else
+		rc = write(fd, "\n", 1);
+	(void) rc;					/* silence compiler warnings */
+	close(fd);
+}
+#endif
+
 /*
  *	Create per-connection SSL object, and load the client certificate,
  *	private key, and trusted CA certs.
@@ -1000,6 +1044,20 @@ initialize_SSL(PGconn *conn)
 	}
 	conn->ssl_in_use = true;
 
+	if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
+	{
+#ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
+		SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
+#else
+#ifdef LIBRESSL_VERSION_NUMBER
+		fprintf(stderr, libpq_gettext("WARNING: sslkeylogfile support requires OpenSSL\n"));
+#else
+		fprintf(stderr, libpq_gettext("WARNING: libpq was not built with sslkeylogfile support\n"));
+#endif
+#endif
+	}
+
+
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
 	 * as we have created the SSL object, and it will stick around for as long
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index ade5ad82f07..ac148fe8c32 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -424,6 +424,7 @@ struct pg_conn
 	char	   *load_balance_hosts; /* load balance over hosts */
 	char	   *scram_client_key;	/* base64-encoded SCRAM client key */
 	char	   *scram_server_key;	/* base64-encoded SCRAM server key */
+	char	   *sslkeylogfile;	/* where should the client write ssl keylogs */
 
 	bool		cancelRequest;	/* true if this connection is used to send a
 								 * cancel request, instead of being a normal
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 5422511d4ab..086abf3b8b3 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -147,6 +147,33 @@ my $default_ssl_connstr =
 $common_connstr =
   "$default_ssl_connstr user=ssltestuser dbname=trustdb hostaddr=$SERVERHOSTADDR host=common-name.pg-ssltest.test";
 
+SKIP:
+{
+	skip "Keylogging is not supported with LibreSSL", 5 if $libressl;
+
+	my $tempdir = PostgreSQL::Test::Utils::tempdir;
+	my @status;
+
+	# Properly escape backslashes in the path
+	$tempdir =~ s/\\/\\\\/g;
+
+	# Connect should work with a given sslkeylogfile
+	$node->connect_ok(
+		"$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/key.txt sslmode=require",
+		"connect with server root cert and sslkeylogfile=$tempdir/key.txt");
+
+	# Verify the key file exists
+	ok(-f "$tempdir/key.txt", "keylog file exists at: $tempdir/key.txt");
+
+	# Skip permission checks on Windows/Cygwin
+	skip "Permissions check not enforced on Windows", 2
+	  if ($windows_os || $Config::Config{osname} eq 'cygwin');
+
+	ok( (@status = stat("$tempdir/key.txt")),
+		"keylog file exists and returned status");
+	ok(@status && !($status[2] & 0006), "keylog file is not world readable");
+}
+
 # The server should not accept non-SSL connections.
 $node->connect_fails(
 	"$common_connstr sslmode=disable",
-- 
2.39.3 (Apple Git-146)

#33Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Gustafsson (#32)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 1 Apr 2025, at 22:22, Daniel Gustafsson <daniel@yesql.se> wrote:

I took another pass at this one and did a few small tweaks, so I would to take
it for another spin across CI before looking at committing it. There are no
functionality changes, only polish.

Committed, after another round of testing and looking.

--
Daniel Gustafsson

#34Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#33)
Re: Adding support for SSLKEYLOGFILE in the frontend

Hello,

On Thu, Apr 3, 2025 at 8:51 AM Daniel Gustafsson <daniel@yesql.se> wrote:

Committed, after another round of testing and looking.

I think we may want to consider marking sslkeylogfile as a debug
option (that is, opt->dispchar = "D") in fe-connect.c. Besides being a
true "debug option", this would also prevent a relatively unprivileged
user of postgres_fdw or dblink from logging the backend connection's
keys. WDYT?

--Jacob

#35Daniel Gustafsson
daniel@yesql.se
In reply to: Jacob Champion (#34)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 9 Apr 2025, at 20:41, Jacob Champion <jacob.champion@enterprisedb.com> wrote:

Hello,

On Thu, Apr 3, 2025 at 8:51 AM Daniel Gustafsson <daniel@yesql.se> wrote:

Committed, after another round of testing and looking.

I think we may want to consider marking sslkeylogfile as a debug
option (that is, opt->dispchar = "D") in fe-connect.c. Besides being a
true "debug option", this would also prevent a relatively unprivileged
user of postgres_fdw or dblink from logging the backend connection's
keys. WDYT?

I think that sounds like a good idea, unless anyone thinks otherwise I'll go
ahead and make it so.

--
Daniel Gustafsson

#36Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Gustafsson (#35)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 9 Apr 2025, at 20:45, Daniel Gustafsson <daniel@yesql.se> wrote:

On 9 Apr 2025, at 20:41, Jacob Champion <jacob.champion@enterprisedb.com> wrote:

Hello,

On Thu, Apr 3, 2025 at 8:51 AM Daniel Gustafsson <daniel@yesql.se> wrote:

Committed, after another round of testing and looking.

I think we may want to consider marking sslkeylogfile as a debug
option (that is, opt->dispchar = "D") in fe-connect.c. Besides being a
true "debug option", this would also prevent a relatively unprivileged
user of postgres_fdw or dblink from logging the backend connection's
keys. WDYT?

I think that sounds like a good idea, unless anyone thinks otherwise I'll go
ahead and make it so.

Just to close the loop, this was done yesterday as 2970c75dd982.

--
Daniel Gustafsson

#37Jacob Champion
jacob.champion@enterprisedb.com
In reply to: Daniel Gustafsson (#36)
Re: Adding support for SSLKEYLOGFILE in the frontend

On Mon, Apr 14, 2025 at 1:16 PM Daniel Gustafsson <daniel@yesql.se> wrote:

Just to close the loop, this was done yesterday as 2970c75dd982.

Thanks!

--Jacob

#38Peter Eisentraut
peter@eisentraut.org
In reply to: Daniel Gustafsson (#33)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 03.04.25 17:51, Daniel Gustafsson wrote:

On 1 Apr 2025, at 22:22, Daniel Gustafsson <daniel@yesql.se> wrote:

I took another pass at this one and did a few small tweaks, so I would to take
it for another spin across CI before looking at committing it. There are no
functionality changes, only polish.

Committed, after another round of testing and looking.

A few more observations on this code:

What is the meaning of this:

+   old_umask = umask(077);
+   fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
+   umask(old_umask);

If open() already specifies the file mode, do we still need umask()?
Maybe I'm missing something.

Also, we usually use symbols for the modes.

+       libpq_append_conn_error(conn, "could not open ssl keylog file 
%s: %s",
+                               conn->sslkeylogfile, pg_strerror(errno));

pg_strerror() is not thread-safe, so it shouldn't be used here.
Actually, pg_strerror() is not meant for direct use at all. Probably
easiest to just use %m.

Also, I can't actually trigger these errors. This call just sticks the
errors into the connection structure, but if the connection is otherwise
successful, nothing will print the error. Maybe the best we can do is
print the error to stderr, like similarly in initialize_SSL().

#39Daniel Gustafsson
daniel@yesql.se
In reply to: Peter Eisentraut (#38)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 26 Jun 2025, at 22:49, Peter Eisentraut <peter@eisentraut.org> wrote:

On 03.04.25 17:51, Daniel Gustafsson wrote:

On 1 Apr 2025, at 22:22, Daniel Gustafsson <daniel@yesql.se> wrote:

I took another pass at this one and did a few small tweaks, so I would to take
it for another spin across CI before looking at committing it. There are no
functionality changes, only polish.

Committed, after another round of testing and looking.

A few more observations on this code:

Thanks for review!

What is the meaning of this:

+   old_umask = umask(077);
+   fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
+   umask(old_umask);

If open() already specifies the file mode, do we still need umask()? Maybe I'm missing something.

No, I think that's a leftover from a previous version which I missed. I can't
see that being required.

Also, we usually use symbols for the modes.

+       libpq_append_conn_error(conn, "could not open ssl keylog file %s: %s",
+                               conn->sslkeylogfile, pg_strerror(errno));

pg_strerror() is not thread-safe, so it shouldn't be used here. Actually, pg_strerror() is not meant for direct use at all. Probably easiest to just use %m.

Also, I can't actually trigger these errors. This call just sticks the errors into the connection structure, but if the connection is otherwise successful, nothing will print the error. Maybe the best we can do is print the error to stderr, like similarly in initialize_SSL().

Thats a good point, a successful connection does not inspect the errormessage.
I'll propose changes for these comments in the morning when coffee has been
had.

--
Daniel Gustafsson

#40Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Gustafsson (#39)
1 attachment(s)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 26 Jun 2025, at 23:06, Daniel Gustafsson <daniel@yesql.se> wrote:

I'll propose changes for these comments in the morning when coffee has been
had.

The attached moves to logging on stderr along with a test for this, and also
removes the WARNING prefix that was added to the other stderr loggings since
that seems out of place compared to other stderr loggings in libpq (the
sslpassword trunction WARNING is clearly warranted, these were not). The umask
call is removed as it's not required, and a little bit of whitespace cleanup
from the original commit performed. Re macros for file modes, this is for
fopen and not open though, or am I missing something?

--
Daniel Gustafsson

Attachments:

0001-Fix-sslkeylogfile-logging.patchapplication/octet-stream; name=0001-Fix-sslkeylogfile-logging.patch; x-unix-mode=0644Download
From cd09b440de3369de0cea48fefdeb9afacc106d93 Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Fri, 27 Jun 2025 11:03:21 +0200
Subject: [PATCH] Fix sslkeylogfile logging

When sslkeylogfile has been set but the file fails to open in an
otherwise successful connection, the log entry added to the conn
object is never printed.  Instead print the error on stderr for
increased visibility.  This is a debugging tool so using stderr
for logging is appropriate.

Also while there, remove the WARNING prefix on other key logging
errors to better match libpq logging style and remove the useless
call to umask in the callback.

Issues noted by Peter Eisentraut in post-commit review.

Author: Daniel Gustafsson <daniel@yesql.se>
Reported-by: Peter Eisentraut <peter@eisentraut.org>
Discussion: https://postgr.es/m/70450bee-cfaa-48ce-8980-fc7efcfebb03@eisentraut.org
---
 src/interfaces/libpq/fe-secure-openssl.c | 21 +++++++++++----------
 src/test/ssl/t/001_ssltests.pl           |  7 +++++++
 2 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index b08b3a6901b..cb2cfa55e1a 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -693,34 +693,36 @@ static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
  * purposes.  The file will be written using the NSS keylog format.  LibreSSL
  * 3.5 introduced stub function to set the callback for OpenSSL compatibility
  * but the callback is never invoked.
+ *
+ * Error messages added to the connection object wont be printed anywhere if
+ * the connection is successful.  Errors in processing keylogging are printed
+ * to stderr to overcome this.
  */
 static void
 SSL_CTX_keylog_cb(const SSL *ssl, const char *line)
 {
 	int			fd;
-	mode_t		old_umask;
 	ssize_t		rc;
 	PGconn	   *conn = SSL_get_app_data(ssl);
 
 	if (conn == NULL)
 		return;
 
-	old_umask = umask(077);
+	errno = 0;
 	fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
-	umask(old_umask);
 
 	if (fd == -1)
 	{
-		libpq_append_conn_error(conn, "could not open SSL key logging file \"%s\": %s",
-								conn->sslkeylogfile, pg_strerror(errno));
+		fprintf(stderr, libpq_gettext("could not open SSL key logging file \"%s\": %m\n"),
+				conn->sslkeylogfile);
 		return;
 	}
 
 	/* line is guaranteed by OpenSSL to be NUL terminated */
 	rc = write(fd, line, strlen(line));
 	if (rc < 0)
-		libpq_append_conn_error(conn, "could not write to SSL key logging file \"%s\": %s",
-								conn->sslkeylogfile, pg_strerror(errno));
+		fprintf(stderr, libpq_gettext("could not write to SSL key logging file \"%s\": %m\n"),
+				conn->sslkeylogfile);
 	else
 		rc = write(fd, "\n", 1);
 	(void) rc;					/* silence compiler warnings */
@@ -1050,14 +1052,13 @@ initialize_SSL(PGconn *conn)
 		SSL_CTX_set_keylog_callback(SSL_context, SSL_CTX_keylog_cb);
 #else
 #ifdef LIBRESSL_VERSION_NUMBER
-		fprintf(stderr, libpq_gettext("WARNING: sslkeylogfile support requires OpenSSL\n"));
+		fprintf(stderr, libpq_gettext("sslkeylogfile support requires OpenSSL\n"));
 #else
-		fprintf(stderr, libpq_gettext("WARNING: libpq was not built with sslkeylogfile support\n"));
+		fprintf(stderr, libpq_gettext("libpq was not built with sslkeylogfile support\n"));
 #endif
 #endif
 	}
 
-
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
 	 * as we have created the SSL object, and it will stick around for as long
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 2cb4d0ffd41..b2eb18d3e81 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -173,6 +173,13 @@ SKIP:
 	ok( (@status = stat("$tempdir/key.txt")),
 		"keylog file exists and returned status");
 	ok(@status && !($status[2] & 0006), "keylog file is not world readable");
+
+	# Connect should work with an incorrect sslkeylogfile, with the error to
+	# open the logfile printed to stderr
+	$node->connect_ok(
+		"$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/invalid/key.txt sslmode=require",
+		"connect with server root cert and incorrect sslkeylogfile path",
+		expected_stderr => qr/could not open/);
 }
 
 # The server should not accept non-SSL connections.
-- 
2.39.3 (Apple Git-146)

#41Peter Eisentraut
peter@eisentraut.org
In reply to: Daniel Gustafsson (#40)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 27.06.25 11:15, Daniel Gustafsson wrote:

On 26 Jun 2025, at 23:06, Daniel Gustafsson <daniel@yesql.se> wrote:

I'll propose changes for these comments in the morning when coffee has been
had.

The attached moves to logging on stderr along with a test for this, and also
removes the WARNING prefix that was added to the other stderr loggings since
that seems out of place compared to other stderr loggings in libpq (the
sslpassword trunction WARNING is clearly warranted, these were not).

Hmm, I thought the WARNING prefixes were good. I think these are similar to

"WARNING: password file \"%s\" is not a plain file\n"

in that something during the connection setup is being ignored.

Otherwise, if you just write something but don't tag it with something
like warning or error, it's not clear how the user is supposed to
interpret it. Is it a progress message, is it something bad, etc.?

The umask
call is removed as it's not required, and a little bit of whitespace cleanup
from the original commit performed. Re macros for file modes, this is for
fopen and not open though, or am I missing something?

It looks like the conventions around the code are mixed. 0600 is clear
enough that we can keep it.

I don't think this is necessary:

+ errno = 0;

#42Daniel Gustafsson
daniel@yesql.se
In reply to: Peter Eisentraut (#41)
1 attachment(s)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 29 Jun 2025, at 12:56, Peter Eisentraut <peter@eisentraut.org> wrote:

On 27.06.25 11:15, Daniel Gustafsson wrote:

On 26 Jun 2025, at 23:06, Daniel Gustafsson <daniel@yesql.se> wrote:
I'll propose changes for these comments in the morning when coffee has been
had.

The attached moves to logging on stderr along with a test for this, and also
removes the WARNING prefix that was added to the other stderr loggings since
that seems out of place compared to other stderr loggings in libpq (the
sslpassword trunction WARNING is clearly warranted, these were not).

Hmm, I thought the WARNING prefixes were good. I think these are similar to

"WARNING: password file \"%s\" is not a plain file\n"

in that something during the connection setup is being ignored.

Otherwise, if you just write something but don't tag it with something like warning or error, it's not clear how the user is supposed to interpret it. Is it a progress message, is it something bad, etc.?

Fair. As this is a connection debugging tool and not for regular use I wasn't
thinking of them as warnings per se (I was more classifying them as the "out of
memory" errors which are printed without a prefix), but I also don't mind
adding them back. Done in v2 with other comments addressed as well.

--
Daniel Gustafsson

Attachments:

v2-0001-Fix-sslkeylogfile-error-handling-logging.patchapplication/octet-stream; name=v2-0001-Fix-sslkeylogfile-error-handling-logging.patch; x-unix-mode=0644Download
From 25e8f176e7eab2c3c65f72e0524395e34a47f2ac Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <dgustafsson@postgresql.org>
Date: Fri, 27 Jun 2025 11:03:21 +0200
Subject: [PATCH v2] Fix sslkeylogfile error handling logging

When sslkeylogfile has been set but the file fails to open in an
otherwise successful connection, the log entry added to the conn
object is never printed.  Instead print the error on stderr for
increased visibility.  This is a debugging tool so using stderr
for logging is appropriate.  Also while there, remove the umask
call in the callback as it's not useful.

Issues noted by Peter Eisentraut in post-commit review.

Author: Daniel Gustafsson <daniel@yesql.se>
Reported-by: Peter Eisentraut <peter@eisentraut.org>
Reviewed-by: Peter Eisentraut <peter@eisentraut.org>
Discussion: https://postgr.es/m/70450bee-cfaa-48ce-8980-fc7efcfebb03@eisentraut.org
---
 src/interfaces/libpq/fe-secure-openssl.c | 20 ++++++++++++--------
 src/test/ssl/t/001_ssltests.pl           |  7 +++++++
 2 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index b08b3a6901b..51dd7b9fec0 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -693,34 +693,35 @@ static unsigned char alpn_protos[] = PG_ALPN_PROTOCOL_VECTOR;
  * purposes.  The file will be written using the NSS keylog format.  LibreSSL
  * 3.5 introduced stub function to set the callback for OpenSSL compatibility
  * but the callback is never invoked.
+ *
+ * Error messages added to the connection object wont be printed anywhere if
+ * the connection is successful.  Errors in processing keylogging are printed
+ * to stderr to overcome this.
  */
 static void
 SSL_CTX_keylog_cb(const SSL *ssl, const char *line)
 {
 	int			fd;
-	mode_t		old_umask;
 	ssize_t		rc;
 	PGconn	   *conn = SSL_get_app_data(ssl);
 
 	if (conn == NULL)
 		return;
 
-	old_umask = umask(077);
 	fd = open(conn->sslkeylogfile, O_WRONLY | O_APPEND | O_CREAT, 0600);
-	umask(old_umask);
 
 	if (fd == -1)
 	{
-		libpq_append_conn_error(conn, "could not open SSL key logging file \"%s\": %s",
-								conn->sslkeylogfile, pg_strerror(errno));
+		fprintf(stderr, libpq_gettext("WARNING: could not open SSL key logging file \"%s\": %m\n"),
+				conn->sslkeylogfile);
 		return;
 	}
 
 	/* line is guaranteed by OpenSSL to be NUL terminated */
 	rc = write(fd, line, strlen(line));
 	if (rc < 0)
-		libpq_append_conn_error(conn, "could not write to SSL key logging file \"%s\": %s",
-								conn->sslkeylogfile, pg_strerror(errno));
+		fprintf(stderr, libpq_gettext("WARNING: could not write to SSL key logging file \"%s\": %m\n"),
+				conn->sslkeylogfile);
 	else
 		rc = write(fd, "\n", 1);
 	(void) rc;					/* silence compiler warnings */
@@ -1044,6 +1045,10 @@ initialize_SSL(PGconn *conn)
 	}
 	conn->ssl_in_use = true;
 
+	/*
+	 * If SSL key logging is requested, set up the callback if a compatible
+	 * version of OpenSSL is used and libpq was compiled to support it.
+	 */
 	if (conn->sslkeylogfile && strlen(conn->sslkeylogfile) > 0)
 	{
 #ifdef HAVE_SSL_CTX_SET_KEYLOG_CALLBACK
@@ -1057,7 +1062,6 @@ initialize_SSL(PGconn *conn)
 #endif
 	}
 
-
 	/*
 	 * SSL contexts are reference counted by OpenSSL. We can free it as soon
 	 * as we have created the SSL object, and it will stick around for as long
diff --git a/src/test/ssl/t/001_ssltests.pl b/src/test/ssl/t/001_ssltests.pl
index 2cb4d0ffd41..b2eb18d3e81 100644
--- a/src/test/ssl/t/001_ssltests.pl
+++ b/src/test/ssl/t/001_ssltests.pl
@@ -173,6 +173,13 @@ SKIP:
 	ok( (@status = stat("$tempdir/key.txt")),
 		"keylog file exists and returned status");
 	ok(@status && !($status[2] & 0006), "keylog file is not world readable");
+
+	# Connect should work with an incorrect sslkeylogfile, with the error to
+	# open the logfile printed to stderr
+	$node->connect_ok(
+		"$common_connstr sslrootcert=ssl/root+server_ca.crt sslkeylogfile=$tempdir/invalid/key.txt sslmode=require",
+		"connect with server root cert and incorrect sslkeylogfile path",
+		expected_stderr => qr/could not open/);
 }
 
 # The server should not accept non-SSL connections.
-- 
2.39.3 (Apple Git-146)

#43Peter Eisentraut
peter@eisentraut.org
In reply to: Daniel Gustafsson (#42)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 01.07.25 10:47, Daniel Gustafsson wrote:

On 29 Jun 2025, at 12:56, Peter Eisentraut <peter@eisentraut.org> wrote:

On 27.06.25 11:15, Daniel Gustafsson wrote:

On 26 Jun 2025, at 23:06, Daniel Gustafsson <daniel@yesql.se> wrote:
I'll propose changes for these comments in the morning when coffee has been
had.

The attached moves to logging on stderr along with a test for this, and also
removes the WARNING prefix that was added to the other stderr loggings since
that seems out of place compared to other stderr loggings in libpq (the
sslpassword trunction WARNING is clearly warranted, these were not).

Hmm, I thought the WARNING prefixes were good. I think these are similar to

"WARNING: password file \"%s\" is not a plain file\n"

in that something during the connection setup is being ignored.

Otherwise, if you just write something but don't tag it with something like warning or error, it's not clear how the user is supposed to interpret it. Is it a progress message, is it something bad, etc.?

Fair. As this is a connection debugging tool and not for regular use I wasn't
thinking of them as warnings per se (I was more classifying them as the "out of
memory" errors which are printed without a prefix), but I also don't mind
adding them back. Done in v2 with other comments addressed as well.

This patch version looks good to me.

#44Daniel Gustafsson
daniel@yesql.se
In reply to: Peter Eisentraut (#43)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 5 Jul 2025, at 10:56, Peter Eisentraut <peter@eisentraut.org> wrote:

This patch version looks good to me.

Thanks for review, I'll get it committed shortly.

--
Daniel Gustafsson

#45Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Gustafsson (#44)
Re: Adding support for SSLKEYLOGFILE in the frontend

On 10 Jul 2025, at 09:33, Daniel Gustafsson <daniel@yesql.se> wrote:

On 5 Jul 2025, at 10:56, Peter Eisentraut <peter@eisentraut.org> wrote:

This patch version looks good to me.

Thanks for review, I'll get it committed shortly.

This was committed yesterday ahead of the beta release.

--
Daniel Gustafsson