public inbox for [email protected]  
help / color / mirror / Atom feed
Re: Add ssl_(supported|shared)_groups to sslinfo
10+ messages / 4 participants
[nested] [flat]

* Re: Add ssl_(supported|shared)_groups to sslinfo
@ 2026-02-27 18:57  Dmitry Dolgov <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Dmitry Dolgov @ 2026-02-27 18:57 UTC (permalink / raw)
  To: Jacob Champion <[email protected]>; +Cc: Daniel Gustafsson <[email protected]>; pgsql-hackers

> On Mon, Feb 23, 2026 at 11:22:22AM -0800, Jacob Champion wrote:
> On Mon, Feb 23, 2026 at 9:58 AM Dmitry Dolgov <[email protected]> wrote:
> > No deep reason, it was just useful for some particular experiments and
> > for gathering understanding of what's going on. Would you find it
> > reasonable to have both, shared groups and the negotiated group, or
> > having only the latter is strictly better?
> 
> Well, take this with a grain of salt, because I tend to use tools
> other than sslinfo for TLS debugging. But it seems to me that all of
> the sslinfo functions cater to facts about the current connection: the
> client certificate, the cipher, the protocol version.
> 
> These new functions instead focus on what *might* have been, which
> makes them kind of awkward. Maybe sslinfo should be expanded to give
> us those tools as well, but I wonder if handshake debugging might be a
> better fit for some debug logging on the server side. Or if there
> might be an overall feature here -- "why did the negotiation behave
> this way?" -- that could be better served by something that's not a
> new array of sslinfo functions that have to be correlated with each
> other.

I see what you mean, an interesting point. After some pondering and
looking at the history of sslinfo it looks like its purpose was already
extended once beyond what was originally intended. AFAICT the initial
implementation was concerning itself only with the information about SSL
certificates (surprisingly even now the extension comment and
documentation say "information about SSL certificates"), and now it also
features the current cipher and version. I take it as an argument that
expanding sslinfo goal and focus is not a problem, as long as it's
clearly communicated and documented. What do you think?

> (Also, while I was taking a look at ssl_extension_info(), I realized
> that it's focused on certificate extensions and not protocol
> extensions. It's kind of unfortunately named.)

Yeah, that's unfortunate. I've ended up introducing a similarly looking
ssl_group_info, which returns a set of record representing groups.

From 69267b00cfe04601dc7be2fd178c55e5b5616e32 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <[email protected]>
Date: Thu, 19 Feb 2026 16:33:17 +0100
Subject: [PATCH v2] contrib/sslinfo: Add ssl_group_info

Add a new function to sslinfo ssl_group_info to show SSL groups,
including negotiated, supported and shared. It's useful for diagnostic
purposes, to identify what's being used and supported, e.g. which key
share is being negotiated. Few examples, for openssl 3.2.4:

    select * from ssl_group_info();
	type    |        name
    ------------+--------------------
     negotiated | X25519MLKEM768
     shared     | X25519MLKEM768
     shared     | x25519
     supported  | X25519MLKEM768
     supported  | x25519
     [...]

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |   2 +-
 contrib/sslinfo/meson.build           |   2 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql |  10 ++
 contrib/sslinfo/sslinfo.c             | 167 +++++++++++++++++++++++++-
 contrib/sslinfo/sslinfo.control       |   2 +-
 doc/src/sgml/sslinfo.sgml             |  17 +++
 src/tools/pgindent/typedefs.list      |   1 +
 7 files changed, 197 insertions(+), 4 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..dc837209c93 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..29c7da44228 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,7 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..40fd0ea2b9c
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,10 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION
+ssl_group_info(OUT group_type text, OUT name text
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_group_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..e018010d4be 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -28,13 +28,28 @@ static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
 static Datum ASN1_STRING_to_text(ASN1_STRING *str);
 
 /*
- * Function context for data persisting over repeated calls.
+ * Function context for data persisting over repeated calls of
+ * ssl_extension_info.
  */
 typedef struct
 {
 	TupleDesc	tupdesc;
 } SSLExtensionInfoContext;
 
+/*
+ * Function context for data persisting over repeated calls of
+ * ssl_group_info.
+ */
+typedef struct
+{
+	TupleDesc	tupdesc;
+	int			nshared;
+	int			nsupported;
+
+	/* Supported groups have to be stored separately */
+	int		   *supported_groups;
+} SSLGroupInfoContext;
+
 /*
  * Indicates whether current session uses SSL
  *
@@ -474,3 +489,153 @@ ssl_extension_info(PG_FUNCTION_ARGS)
 	/* All done */
 	SRF_RETURN_DONE(funcctx);
 }
+
+/*
+ * Returns information about TLS groups.
+ *
+ * Returns setof record made of the following values:
+ * - type of the group: negotiated, shared, supported.
+ * - name of the group.
+ */
+PG_FUNCTION_INFO_V1(ssl_group_info);
+Datum
+ssl_group_info(PG_FUNCTION_ARGS)
+{
+	SSL		   *ssl = MyProcPort->ssl;
+	FuncCallContext *funcctx;
+	int			call_cntr = 0;
+	int			max_calls = 0;
+	MemoryContext oldcontext;
+	SSLGroupInfoContext *fctx;
+
+	if (SRF_IS_FIRSTCALL())
+	{
+
+		TupleDesc	tupdesc;
+
+		/* create a function context for cross-call persistence */
+		funcctx = SRF_FIRSTCALL_INIT();
+
+		/*
+		 * Switch to memory context appropriate for multiple function calls
+		 */
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		/* Create a user function context for cross-call persistence */
+		fctx = palloc_object(SSLGroupInfoContext);
+
+		/* Construct tuple descriptor */
+		if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("function returning record called in context that cannot accept type record")));
+		fctx->tupdesc = BlessTupleDesc(tupdesc);
+
+		if (!MyProcPort->ssl_in_use)
+		{
+			/* fast track when no results */
+			MemoryContextSwitchTo(oldcontext);
+			SRF_RETURN_DONE(funcctx);
+		}
+
+		if (ssl != NULL)
+		{
+			fctx->nsupported = SSL_get1_groups(ssl, NULL);
+			fctx->nshared = SSL_get_shared_group(ssl, -1);
+
+			fctx->supported_groups =
+				palloc(fctx->nsupported * sizeof(*fctx->supported_groups));
+			SSL_get1_groups(ssl, fctx->supported_groups);
+
+			/*
+			 * Set max_calls as the number of supported groups plus the number
+			 * of shared groups plus one negotiated group.
+			 */
+			max_calls = fctx->nsupported + fctx->nshared + 1;
+		}
+
+		if (max_calls > 0)
+		{
+			/* got results, keep track of them */
+			funcctx->max_calls = max_calls;
+			funcctx->user_fctx = fctx;
+		}
+		else
+		{
+			/* fast track when no results */
+			MemoryContextSwitchTo(oldcontext);
+			SRF_RETURN_DONE(funcctx);
+		}
+
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	/* stuff done on every call of the function */
+	funcctx = SRF_PERCALL_SETUP();
+
+	/*
+	 * Initialize per-call variables.
+	 */
+	call_cntr = funcctx->call_cntr;
+	max_calls = funcctx->max_calls;
+	fctx = funcctx->user_fctx;
+
+	/* do while there are more left to send */
+	if (call_cntr < max_calls)
+	{
+		Datum		values[2];
+		bool		nulls[2];
+		HeapTuple	tuple;
+		Datum		result,
+					group_type;
+		int			nid;
+		const char *group_name;
+
+		/* Send the negotiated group first */
+		if (call_cntr == 0)
+		{
+			nid = SSL_get_negotiated_group(ssl);
+			group_type = CStringGetTextDatum("negotiated");
+		}
+		/* Then the shared groups */
+		else if (call_cntr < fctx->nshared + 1)
+		{
+			nid = SSL_get_shared_group(ssl, call_cntr - 1);
+			group_type = CStringGetTextDatum("shared");
+		}
+		/* And finally the supported groups */
+		else if (call_cntr < fctx->nsupported + fctx->nshared + 1)
+		{
+			nid = fctx->supported_groups[call_cntr - fctx->nshared - 1];
+			group_type = CStringGetTextDatum("supported");
+		}
+		else
+			SRF_RETURN_DONE(funcctx);
+
+		/*
+		 * SSL_group_to_name can return NULL in case of an error, e.g. when no
+		 * such name was registered for some reason.
+		 */
+		group_name = SSL_group_to_name(ssl, nid);
+		if (group_name == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("unknown OpenSSL group at position %d",
+							call_cntr)));
+
+		values[0] = group_type;
+		nulls[0] = false;
+
+		values[1] = CStringGetTextDatum(group_name);
+		nulls[1] = false;
+
+		/* Build tuple */
+		tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
+		result = HeapTupleGetDatum(tuple);
+
+		SRF_RETURN_NEXT(funcctx, result);
+	}
+
+	/* All done */
+	SRF_RETURN_DONE(funcctx);
+}
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/doc/src/sgml/sslinfo.sgml b/doc/src/sgml/sslinfo.sgml
index 85d49f66537..8ba2302fe08 100644
--- a/doc/src/sgml/sslinfo.sgml
+++ b/doc/src/sgml/sslinfo.sgml
@@ -240,6 +240,23 @@ emailAddress
     </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term>
+     <function>ssl_group_info() returns setof record</function>
+     <indexterm>
+      <primary>ssl_group_info</primary>
+     </indexterm>
+    </term>
+    <listitem>
+    <para>
+     Provide information about TLS groups: group type and group name.
+     The group type value could be one of the following: negotiated
+     (the group used for the handshake key exchange process), shared
+     or supported. The latter two are used mostly for diagnostic purposes.
+    </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
  </sect2>
 
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 241945734ec..74eb3043dbb 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2694,6 +2694,7 @@ SQLValueFunction
 SQLValueFunctionOp
 SSL
 SSLExtensionInfoContext
+SSLGroupInfoContext
 SSL_CTX
 STARTUPINFO
 STRLEN

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0



Attachments:

  [text/plain] v2-0001-contrib-sslinfo-Add-ssl_group_info.patch (9.0K, 2-v2-0001-contrib-sslinfo-Add-ssl_group_info.patch)
  download | inline diff:
From 69267b00cfe04601dc7be2fd178c55e5b5616e32 Mon Sep 17 00:00:00 2001
From: Dmitrii Dolgov <[email protected]>
Date: Thu, 19 Feb 2026 16:33:17 +0100
Subject: [PATCH v2] contrib/sslinfo: Add ssl_group_info

Add a new function to sslinfo ssl_group_info to show SSL groups,
including negotiated, supported and shared. It's useful for diagnostic
purposes, to identify what's being used and supported, e.g. which key
share is being negotiated. Few examples, for openssl 3.2.4:

    select * from ssl_group_info();
	type    |        name
    ------------+--------------------
     negotiated | X25519MLKEM768
     shared     | X25519MLKEM768
     shared     | x25519
     supported  | X25519MLKEM768
     supported  | x25519
     [...]

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |   2 +-
 contrib/sslinfo/meson.build           |   2 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql |  10 ++
 contrib/sslinfo/sslinfo.c             | 167 +++++++++++++++++++++++++-
 contrib/sslinfo/sslinfo.control       |   2 +-
 doc/src/sgml/sslinfo.sgml             |  17 +++
 src/tools/pgindent/typedefs.list      |   1 +
 7 files changed, 197 insertions(+), 4 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..dc837209c93 100644
--- a/contrib/sslinfo/Makefile
+++ b/contrib/sslinfo/Makefile
@@ -6,7 +6,7 @@ OBJS = \
 	sslinfo.o
 
 EXTENSION = sslinfo
-DATA = sslinfo--1.2.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
+DATA = sslinfo--1.2--1.3.sql sslinfo--1.1--1.2.sql sslinfo--1.0--1.1.sql
 PGFILEDESC = "sslinfo - information about client SSL certificate"
 
 ifdef USE_PGXS
diff --git a/contrib/sslinfo/meson.build b/contrib/sslinfo/meson.build
index 6e9cb96430a..29c7da44228 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,7 @@ contrib_targets += sslinfo
 install_data(
   'sslinfo--1.0--1.1.sql',
   'sslinfo--1.1--1.2.sql',
-  'sslinfo--1.2.sql',
+  'sslinfo--1.2--1.3.sql',
   'sslinfo.control',
   kwargs: contrib_data_args,
 )
diff --git a/contrib/sslinfo/sslinfo--1.2--1.3.sql b/contrib/sslinfo/sslinfo--1.2--1.3.sql
new file mode 100644
index 00000000000..40fd0ea2b9c
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,10 @@
+/* contrib/sslinfo/sslinfo--1.2--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION sslinfo UPDATE TO '1.3'" to load this file. \quit
+
+CREATE FUNCTION
+ssl_group_info(OUT group_type text, OUT name text
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_group_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..e018010d4be 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -28,13 +28,28 @@ static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
 static Datum ASN1_STRING_to_text(ASN1_STRING *str);
 
 /*
- * Function context for data persisting over repeated calls.
+ * Function context for data persisting over repeated calls of
+ * ssl_extension_info.
  */
 typedef struct
 {
 	TupleDesc	tupdesc;
 } SSLExtensionInfoContext;
 
+/*
+ * Function context for data persisting over repeated calls of
+ * ssl_group_info.
+ */
+typedef struct
+{
+	TupleDesc	tupdesc;
+	int			nshared;
+	int			nsupported;
+
+	/* Supported groups have to be stored separately */
+	int		   *supported_groups;
+} SSLGroupInfoContext;
+
 /*
  * Indicates whether current session uses SSL
  *
@@ -474,3 +489,153 @@ ssl_extension_info(PG_FUNCTION_ARGS)
 	/* All done */
 	SRF_RETURN_DONE(funcctx);
 }
+
+/*
+ * Returns information about TLS groups.
+ *
+ * Returns setof record made of the following values:
+ * - type of the group: negotiated, shared, supported.
+ * - name of the group.
+ */
+PG_FUNCTION_INFO_V1(ssl_group_info);
+Datum
+ssl_group_info(PG_FUNCTION_ARGS)
+{
+	SSL		   *ssl = MyProcPort->ssl;
+	FuncCallContext *funcctx;
+	int			call_cntr = 0;
+	int			max_calls = 0;
+	MemoryContext oldcontext;
+	SSLGroupInfoContext *fctx;
+
+	if (SRF_IS_FIRSTCALL())
+	{
+
+		TupleDesc	tupdesc;
+
+		/* create a function context for cross-call persistence */
+		funcctx = SRF_FIRSTCALL_INIT();
+
+		/*
+		 * Switch to memory context appropriate for multiple function calls
+		 */
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
+		/* Create a user function context for cross-call persistence */
+		fctx = palloc_object(SSLGroupInfoContext);
+
+		/* Construct tuple descriptor */
+		if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("function returning record called in context that cannot accept type record")));
+		fctx->tupdesc = BlessTupleDesc(tupdesc);
+
+		if (!MyProcPort->ssl_in_use)
+		{
+			/* fast track when no results */
+			MemoryContextSwitchTo(oldcontext);
+			SRF_RETURN_DONE(funcctx);
+		}
+
+		if (ssl != NULL)
+		{
+			fctx->nsupported = SSL_get1_groups(ssl, NULL);
+			fctx->nshared = SSL_get_shared_group(ssl, -1);
+
+			fctx->supported_groups =
+				palloc(fctx->nsupported * sizeof(*fctx->supported_groups));
+			SSL_get1_groups(ssl, fctx->supported_groups);
+
+			/*
+			 * Set max_calls as the number of supported groups plus the number
+			 * of shared groups plus one negotiated group.
+			 */
+			max_calls = fctx->nsupported + fctx->nshared + 1;
+		}
+
+		if (max_calls > 0)
+		{
+			/* got results, keep track of them */
+			funcctx->max_calls = max_calls;
+			funcctx->user_fctx = fctx;
+		}
+		else
+		{
+			/* fast track when no results */
+			MemoryContextSwitchTo(oldcontext);
+			SRF_RETURN_DONE(funcctx);
+		}
+
+		MemoryContextSwitchTo(oldcontext);
+	}
+
+	/* stuff done on every call of the function */
+	funcctx = SRF_PERCALL_SETUP();
+
+	/*
+	 * Initialize per-call variables.
+	 */
+	call_cntr = funcctx->call_cntr;
+	max_calls = funcctx->max_calls;
+	fctx = funcctx->user_fctx;
+
+	/* do while there are more left to send */
+	if (call_cntr < max_calls)
+	{
+		Datum		values[2];
+		bool		nulls[2];
+		HeapTuple	tuple;
+		Datum		result,
+					group_type;
+		int			nid;
+		const char *group_name;
+
+		/* Send the negotiated group first */
+		if (call_cntr == 0)
+		{
+			nid = SSL_get_negotiated_group(ssl);
+			group_type = CStringGetTextDatum("negotiated");
+		}
+		/* Then the shared groups */
+		else if (call_cntr < fctx->nshared + 1)
+		{
+			nid = SSL_get_shared_group(ssl, call_cntr - 1);
+			group_type = CStringGetTextDatum("shared");
+		}
+		/* And finally the supported groups */
+		else if (call_cntr < fctx->nsupported + fctx->nshared + 1)
+		{
+			nid = fctx->supported_groups[call_cntr - fctx->nshared - 1];
+			group_type = CStringGetTextDatum("supported");
+		}
+		else
+			SRF_RETURN_DONE(funcctx);
+
+		/*
+		 * SSL_group_to_name can return NULL in case of an error, e.g. when no
+		 * such name was registered for some reason.
+		 */
+		group_name = SSL_group_to_name(ssl, nid);
+		if (group_name == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("unknown OpenSSL group at position %d",
+							call_cntr)));
+
+		values[0] = group_type;
+		nulls[0] = false;
+
+		values[1] = CStringGetTextDatum(group_name);
+		nulls[1] = false;
+
+		/* Build tuple */
+		tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
+		result = HeapTupleGetDatum(tuple);
+
+		SRF_RETURN_NEXT(funcctx, result);
+	}
+
+	/* All done */
+	SRF_RETURN_DONE(funcctx);
+}
diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control
index c7754f924cf..b53e95b7da8 100644
--- a/contrib/sslinfo/sslinfo.control
+++ b/contrib/sslinfo/sslinfo.control
@@ -1,5 +1,5 @@
 # sslinfo extension
 comment = 'information about SSL certificates'
-default_version = '1.2'
+default_version = '1.3'
 module_pathname = '$libdir/sslinfo'
 relocatable = true
diff --git a/doc/src/sgml/sslinfo.sgml b/doc/src/sgml/sslinfo.sgml
index 85d49f66537..8ba2302fe08 100644
--- a/doc/src/sgml/sslinfo.sgml
+++ b/doc/src/sgml/sslinfo.sgml
@@ -240,6 +240,23 @@ emailAddress
     </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term>
+     <function>ssl_group_info() returns setof record</function>
+     <indexterm>
+      <primary>ssl_group_info</primary>
+     </indexterm>
+    </term>
+    <listitem>
+    <para>
+     Provide information about TLS groups: group type and group name.
+     The group type value could be one of the following: negotiated
+     (the group used for the handshake key exchange process), shared
+     or supported. The latter two are used mostly for diagnostic purposes.
+    </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
  </sect2>
 
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 241945734ec..74eb3043dbb 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2694,6 +2694,7 @@ SQLValueFunction
 SQLValueFunctionOp
 SSL
 SSLExtensionInfoContext
+SSLGroupInfoContext
 SSL_CTX
 STARTUPINFO
 STRLEN

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0



^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: Add ssl_(supported|shared)_groups to sslinfo
@ 2026-02-28 00:51  Jacob Champion <[email protected]>
  parent: Dmitry Dolgov <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Jacob Champion @ 2026-02-28 00:51 UTC (permalink / raw)
  To: Dmitry Dolgov <[email protected]>; +Cc: Daniel Gustafsson <[email protected]>; pgsql-hackers

On Fri, Feb 27, 2026 at 10:57 AM Dmitry Dolgov <[email protected]> wrote:
> I take it as an argument that
> expanding sslinfo goal and focus is not a problem, as long as it's
> clearly communicated and documented. What do you think?

Yeah -- as long as the API stays coherent, I have no issue with
expanding sslinfo's capabilities.

>     select * from ssl_group_info();
>         type    |        name
>     ------------+--------------------
>      negotiated | X25519MLKEM768
>      shared     | X25519MLKEM768
>      shared     | x25519
>      supported  | X25519MLKEM768
>      supported  | x25519

Hmm, I'm developing strong opinions over something I said I didn't
feel strongly about. Sorry...

The type names "negotiated", "shared" and "supported" don't really
tell me much as an end user. I know, as a dev, that "negotiated" is
the one that was chosen, "supported" is what the client provided, and
"shared" is the intersection of the client and server sets. But I
think it'd be good to choose names that are either based on the
official TLS specification, or immediately clear to someone who is not
well-versed in TLS to begin with, as opposed to using OpenSSL's
internal API names.

Also, I feel like this is still missing the server side of the Venn diagram.

Also also: if we later expose a version of this table for the
ciphersuites or other negotiated parameters, is this how we'd want the
table to look? What did you care most about when you were debugging?

--Jacob






^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: Add ssl_(supported|shared)_groups to sslinfo
@ 2026-03-03 10:43  Dmitry Dolgov <[email protected]>
  parent: Jacob Champion <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Dmitry Dolgov @ 2026-03-03 10:43 UTC (permalink / raw)
  To: Jacob Champion <[email protected]>; +Cc: Daniel Gustafsson <[email protected]>; pgsql-hackers

> On Fri, Feb 27, 2026 at 04:51:40PM -0800, Jacob Champion wrote:
> >     select * from ssl_group_info();
> >         type    |        name
> >     ------------+--------------------
> >      negotiated | X25519MLKEM768
> >      shared     | X25519MLKEM768
> >      shared     | x25519
> >      supported  | X25519MLKEM768
> >      supported  | x25519
> 
> Hmm, I'm developing strong opinions over something I said I didn't
> feel strongly about. Sorry...

No worries, it's a valuable discussion so far :)

> The type names "negotiated", "shared" and "supported" don't really
> tell me much as an end user. I know, as a dev, that "negotiated" is
> the one that was chosen, "supported" is what the client provided, and
> "shared" is the intersection of the client and server sets. But I
> think it'd be good to choose names that are either based on the
> official TLS specification, or immediately clear to someone who is not
> well-versed in TLS to begin with, as opposed to using OpenSSL's
> internal API names.

Naming is hard of course, but my plan was to stick to short names like
those above, and unwrap them in the documentation:

* Supported: list of named groups supported by the client for key
  exchange in the form of "supported_groups" extension.

  Supported group is the exact terminology used in the TLS spec.

* Shared: lisf of named groups shared with the server side.

  This one actually doesn't appear in the spec. The closest name
  featured is "selected_groups", but only in the context of the retry
  requests. Thus I took this form the OpenSSL docs.

* Negotiated: the group used for the handshake key exchange process.

  Surprsingly, I don't see any exact terminology for this in the TLS
  spec, it just says "the named group for the key being exchanged". The
  name is taken from the OpenSSL documentation.

How does it sound?

> Also, I feel like this is still missing the server side of the Venn diagram.

Indeed, the TLS spec says that the server is permitted to send the
"supported_groups" extension to the client, so this might be
interesting. But it doesn't look like there is any OpenSSL API to get
those, beyond SSL_ctrl, which is not supposed to be called directly.

> Also also: if we later expose a version of this table for the
> ciphersuites or other negotiated parameters, is this how we'd want the
> table to look? What did you care most about when you were debugging?

I don't see why not. The reason why I've started tinkering on that was
only to get the negotiated group. But I'm convinced that, since
PostgreSQL sets the groups extension, it totally makes sense to provide
some API for diagnostig reasons to check what's in there beyond only one
single group.





^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: Add ssl_(supported|shared)_groups to sslinfo
@ 2026-03-18 11:18  Dmitry Dolgov <[email protected]>
  parent: Dmitry Dolgov <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Dmitry Dolgov @ 2026-03-18 11:18 UTC (permalink / raw)
  To: Jacob Champion <[email protected]>; +Cc: Daniel Gustafsson <[email protected]>; pgsql-hackers

> On Tue, Mar 03, 2026 at 11:43:10AM +0100, Dmitry Dolgov wrote:
> Naming is hard of course, but my plan was to stick to short names like
> those above, and unwrap them in the documentation:
> 
> * Supported: list of named groups supported by the client for key
>   exchange in the form of "supported_groups" extension.
> 
>   Supported group is the exact terminology used in the TLS spec.
> 
> * Shared: lisf of named groups shared with the server side.
> 
>   This one actually doesn't appear in the spec. The closest name
>   featured is "selected_groups", but only in the context of the retry
>   requests. Thus I took this form the OpenSSL docs.
> 
> * Negotiated: the group used for the handshake key exchange process.
> 
>   Surprsingly, I don't see any exact terminology for this in the TLS
>   spec, it just says "the named group for the key being exchanged". The
>   name is taken from the OpenSSL documentation.
> 
> How does it sound?

Added those into the documentation, will create a CF item.


^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: Add ssl_(supported|shared)_groups to sslinfo
@ 2026-03-19 15:15  Dmitry Dolgov <[email protected]>
  parent: Dmitry Dolgov <[email protected]>
  0 siblings, 1 reply; 10+ messages in thread

From: Dmitry Dolgov @ 2026-03-19 15:15 UTC (permalink / raw)
  To: Jacob Champion <[email protected]>; +Cc: Daniel Gustafsson <[email protected]>; pgsql-hackers

> On Wed, Mar 18, 2026 at 12:18:52PM +0100, Dmitry Dolgov wrote:
> Added those into the documentation, will create a CF item.

And had to fix one thing right away, the installable version was
missing. 


^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: Add ssl_(supported|shared)_groups to sslinfo
@ 2026-03-21 13:25  Dmitry Dolgov <[email protected]>
  parent: Dmitry Dolgov <[email protected]>
  0 siblings, 2 replies; 10+ messages in thread

From: Dmitry Dolgov @ 2026-03-21 13:25 UTC (permalink / raw)
  To: Jacob Champion <[email protected]>; +Cc: Daniel Gustafsson <[email protected]>; pgsql-hackers

> On Thu, Mar 19, 2026 at 04:15:35PM +0100, Dmitry Dolgov wrote:
> > On Wed, Mar 18, 2026 at 12:18:52PM +0100, Dmitry Dolgov wrote:
> > Added those into the documentation, will create a CF item.
> 
> And had to fix one thing right away, the installable version was
> missing. 

Looks like the functions required for this are not available on some
supported versions of openssl and some platforms, so I've expanded the
patch to verify their presence. A somewhat confusing part is that some
of the API functions are real functions and some are just wrappers, for
the latter the declaration has to be checked.

As a note, when I was testing it, for some reason autoconf 2.69 was
moving few unrelevant blocks around without changing anything. I've
tried to avoid those, hopefully it worked out just fine.


^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: Add ssl_(supported|shared)_groups to sslinfo
@ 2026-05-08 21:36  Cary Huang <[email protected]>
  parent: Dmitry Dolgov <[email protected]>
  1 sibling, 1 reply; 10+ messages in thread

From: Cary Huang @ 2026-05-08 21:36 UTC (permalink / raw)
  To: Dmitry Dolgov <[email protected]>; +Cc: Jacob Champion <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

Hi

Given that sslinfo is designed to expose diagnostic information
about the current TLS connection, I am supportive of extending
its functionality.

Just some comments about the patch:

> /* Send the negotiated group first */
> if (call_cntr == 0)
> {
> 	nid = SSL_get_negotiated_group(ssl);
> 	group_type = CStringGetTextDatum("negotiated");
> }
> /* Then the shared groups */
> else if (call_cntr < fctx->nshared + 1)
> {
> 	nid = SSL_get_shared_group(ssl, call_cntr - 1);
> 	group_type = CStringGetTextDatum("shared");
> }
> /* And finally the supported groups */
> else if (call_cntr < fctx->nsupported + fctx->nshared + 1)
> {
> 	nid = fctx->supported_groups[call_cntr - fctx->nshared - 1];
> 	group_type = CStringGetTextDatum("supported");
> }
> else
> 	SRF_RETURN_DONE(funcctx);
> 
> /*
>  * SSL_group_to_name can return NULL in case of an error, e.g. when no
>  * such name was registered for some reason.
>  */
> group_name = SSL_group_to_name(ssl, nid);
> if (group_name == NULL)
> 	ereport(ERROR,
> 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
> 			 errmsg("unknown OpenSSL group at position %d",
> 					call_cntr)));

It is possible that SSL_get_negotiated_group() and
SSL_get_shared_group() would return NID_undef when there is no
negotiated group. The current code will pass that to
SSL_group_to_name() and raise an error if it returns NULL.

Instead of failing the whole function, would it be better to
just omit that row since the function returns a SETOF record?

if nid == NID_undef, we could just omit the row instead of
making a call to SSL_group_to_name(), which most likely will
fail.

Also, I found a small typo on documentation:

> Lisf of named groups shared with the server side.

should be corrected to:

List of named groups shared with the server side.

thanks!

Cary Huang
-------------
HighGo Software Inc. (Canada)
[email protected]
www.highgo.ca







^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: Add ssl_(supported|shared)_groups to sslinfo
@ 2026-05-11 15:50  Dmitry Dolgov <[email protected]>
  parent: Cary Huang <[email protected]>
  0 siblings, 0 replies; 10+ messages in thread

From: Dmitry Dolgov @ 2026-05-11 15:50 UTC (permalink / raw)
  To: Cary Huang <[email protected]>; +Cc: Jacob Champion <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

> On Fri, May 08, 2026 at 02:36:10PM -0700, Cary Huang wrote:
> Hi
> 
> Given that sslinfo is designed to expose diagnostic information
> about the current TLS connection, I am supportive of extending
> its functionality.

Thanks for looking into it.

> > /* Send the negotiated group first */
> > if (call_cntr == 0)
> > {
> > 	nid = SSL_get_negotiated_group(ssl);
> > 	group_type = CStringGetTextDatum("negotiated");
> > }
> > /* Then the shared groups */
> > else if (call_cntr < fctx->nshared + 1)
> > {
> > 	nid = SSL_get_shared_group(ssl, call_cntr - 1);
> > 	group_type = CStringGetTextDatum("shared");
> > }
> > /* And finally the supported groups */
> > else if (call_cntr < fctx->nsupported + fctx->nshared + 1)
> > {
> > 	nid = fctx->supported_groups[call_cntr - fctx->nshared - 1];
> > 	group_type = CStringGetTextDatum("supported");
> > }
> > else
> > 	SRF_RETURN_DONE(funcctx);
> > 
> > /*
> >  * SSL_group_to_name can return NULL in case of an error, e.g. when no
> >  * such name was registered for some reason.
> >  */
> > group_name = SSL_group_to_name(ssl, nid);
> > if (group_name == NULL)
> > 	ereport(ERROR,
> > 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
> > 			 errmsg("unknown OpenSSL group at position %d",
> > 					call_cntr)));
> 
> It is possible that SSL_get_negotiated_group() and
> SSL_get_shared_group() would return NID_undef when there is no
> negotiated group. The current code will pass that to
> SSL_group_to_name() and raise an error if it returns NULL.
> 
> Instead of failing the whole function, would it be better to
> just omit that row since the function returns a SETOF record?
> 
> if nid == NID_undef, we could just omit the row instead of
> making a call to SSL_group_to_name(), which most likely will
> fail.

It makes sense to me in general, but it looks there are some arguments
against this:

* ssl_extension_info function is also an SRF and returns an error if
  faced NID_undef. It's probably a good idea to be concistent with the
  existing functionality. 

* From what I see SRF API doesn't allow to "skip" a record, available
  options are either to finish the set with SRF_RETURN_DONE, or to
  return a NULL record with SRF_RETURN_NEXT_NULL. That means that when
  facing NID_undef, we can stop altogether and skip all the records
  after this, or have a NULL record in the output, both don't sound
  fitting the purpose here.

With this in mind I'm inclined to leave it as it is, but open for
suggestions.





^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: Add ssl_(supported|shared)_groups to sslinfo
@ 2026-05-12 21:48  Zsolt Parragi <[email protected]>
  parent: Dmitry Dolgov <[email protected]>
  1 sibling, 1 reply; 10+ messages in thread

From: Zsolt Parragi @ 2026-05-12 21:48 UTC (permalink / raw)
  To: Dmitry Dolgov <[email protected]>; +Cc: Jacob Champion <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

Hello!

+#define HAVE_SSL_GROUPS \
+ defined(HAVE_DECL_SSL_GET1_GROUPS) && \
+ defined(HAVE_DECL_SSL_GET_NEGOTIATED_GROUP) && \
+ defined(HAVE_SSL_GROUP_TO_NAME)

I don't think this check works properly
1. autoconf/meson always defines HAVE_DECL to 0/1, so defined() always
returns true
2. in practice it should work, but using defined() in a macro
expansion is undefined behavior





^ permalink  raw  reply  [nested|flat] 10+ messages in thread

* Re: Add ssl_(supported|shared)_groups to sslinfo
@ 2026-05-13 18:08  Dmitry Dolgov <[email protected]>
  parent: Zsolt Parragi <[email protected]>
  0 siblings, 0 replies; 10+ messages in thread

From: Dmitry Dolgov @ 2026-05-13 18:08 UTC (permalink / raw)
  To: Zsolt Parragi <[email protected]>; +Cc: Jacob Champion <[email protected]>; Daniel Gustafsson <[email protected]>; pgsql-hackers

> On Tue, May 12, 2026 at 10:48:20PM +0100, Zsolt Parragi wrote:
> +#define HAVE_SSL_GROUPS \
> + defined(HAVE_DECL_SSL_GET1_GROUPS) && \
> + defined(HAVE_DECL_SSL_GET_NEGOTIATED_GROUP) && \
> + defined(HAVE_SSL_GROUP_TO_NAME)
> 
> I don't think this check works properly
> 1. autoconf/meson always defines HAVE_DECL to 0/1, so defined() always
> returns true
> 2. in practice it should work, but using defined() in a macro
> expansion is undefined behavior

That's indeed a faux pas. I was testing this on openssl 1.1.1w, which
was lacking both SSL_get_negotiated_group and SSL_group_to_name, and
this was hiding the problem. The new version should address this
problem, and includes documentation fix from Cary.


^ permalink  raw  reply  [nested|flat] 10+ messages in thread


end of thread, other threads:[~2026-05-13 18:08 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-02-27 18:57 Re: Add ssl_(supported|shared)_groups to sslinfo Dmitry Dolgov <[email protected]>
2026-02-28 00:51 ` Jacob Champion <[email protected]>
2026-03-03 10:43   ` Dmitry Dolgov <[email protected]>
2026-03-18 11:18     ` Dmitry Dolgov <[email protected]>
2026-03-19 15:15       ` Dmitry Dolgov <[email protected]>
2026-03-21 13:25         ` Dmitry Dolgov <[email protected]>
2026-05-08 21:36           ` Cary Huang <[email protected]>
2026-05-11 15:50             ` Dmitry Dolgov <[email protected]>
2026-05-12 21:48           ` Zsolt Parragi <[email protected]>
2026-05-13 18:08             ` Dmitry Dolgov <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox