agora inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
285+ messages / 2 participants
[nested] [flat]

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups
@ 2026-02-19 15:33 Dmitrii Dolgov <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Dmitrii Dolgov @ 2026-02-19 15:33 UTC (permalink / raw)

Add new functions to sslinfo to show TLS groups extension, both
supported and shared. It's useful for identifying what's being used and
supported, e.g. which key share is being negotiated. Few examples, for
openssl 3.2.4:

    =# select ssl_shared_groups();
     ssl_shared_groups
    -------------------
     x25519:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    --------------------------------------
     x25519:[...]

And the same for openssl 3.5 with different defaults:

    =# select ssl_shared_groups();
	    ssl_shared_groups
    ---------------------------------
     X25519MLKEM768:[...]

    =# select ssl_supported_groups();
		ssl_supported_groups
    -------------------------------------
     X25519MLKEM768:[...]

Do not add those functions into the pg_ssl_stats, because they could become
quite large, bloating PgBackendSSLStatus.

The implementation is inspired by ssl_print_groups from openssl.
---
 contrib/sslinfo/Makefile              |  2 +-
 contrib/sslinfo/meson.build           |  3 +-
 contrib/sslinfo/sslinfo--1.2--1.3.sql | 12 ++++++
 contrib/sslinfo/sslinfo--1.3.sql      | 56 ++++++++++++++++++++++++++
 contrib/sslinfo/sslinfo.c             | 31 ++++++++++++++
 contrib/sslinfo/sslinfo.control       |  2 +-
 src/backend/libpq/be-secure-openssl.c | 58 +++++++++++++++++++++++++++
 src/include/libpq/libpq-be.h          |  2 +
 8 files changed, 163 insertions(+), 3 deletions(-)
 create mode 100644 contrib/sslinfo/sslinfo--1.2--1.3.sql
 create mode 100644 contrib/sslinfo/sslinfo--1.3.sql

diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile
index 14305594e2d..d278f02c093 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.3.sql 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..0258813dde7 100644
--- a/contrib/sslinfo/meson.build
+++ b/contrib/sslinfo/meson.build
@@ -25,7 +25,8 @@ 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--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..d2cea79faa7
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.2--1.3.sql
@@ -0,0 +1,12 @@
+/* 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_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo--1.3.sql b/contrib/sslinfo/sslinfo--1.3.sql
new file mode 100644
index 00000000000..98b51900959
--- /dev/null
+++ b/contrib/sslinfo/sslinfo--1.3.sql
@@ -0,0 +1,56 @@
+/* contrib/sslinfo/sslinfo--1.3.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION sslinfo" to load this file. \quit
+
+CREATE FUNCTION ssl_client_serial() RETURNS numeric
+AS 'MODULE_PATHNAME', 'ssl_client_serial'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_is_used() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_is_used'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_version() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_version'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_cipher() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_cipher'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_cert_present() RETURNS boolean
+AS 'MODULE_PATHNAME', 'ssl_client_cert_present'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_field(text) RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_field'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_client_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_client_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_issuer_dn() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_issuer_dn'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_supported_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_supported_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION ssl_shared_groups() RETURNS text
+AS 'MODULE_PATHNAME', 'ssl_shared_groups'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
+
+CREATE FUNCTION
+ssl_extension_info(OUT name text,
+    OUT value text,
+    OUT critical boolean
+) RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'ssl_extension_info'
+LANGUAGE C STRICT PARALLEL RESTRICTED;
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 2b9eb90b093..3efdca6d6bc 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -88,6 +88,37 @@ ssl_cipher(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
+PG_FUNCTION_INFO_V1(ssl_supported_groups);
+Datum
+ssl_supported_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_supported_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
+
+PG_FUNCTION_INFO_V1(ssl_shared_groups);
+Datum
+ssl_shared_groups(PG_FUNCTION_ARGS)
+{
+	const char *groups;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	groups = be_tls_get_shared_groups(MyProcPort);
+	if (groups == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(groups));
+}
 
 /*
  * Indicates whether current client provided a certificate
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/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 4da6ac22ff9..b134fc53f5f 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -51,6 +51,8 @@
 #endif
 #include <openssl/x509v3.h>
 
+#define SSL_SUPPORTED_GROUPS	0
+#define SSL_SHARED_GROUPS		1
 
 /* default init hook can be overridden by a shared library */
 static void default_openssl_tls_init(SSL_CTX *context, bool isServerStart);
@@ -1560,6 +1562,62 @@ be_tls_get_cipher(Port *port)
 		return NULL;
 }
 
+static const char *
+be_tls_get_groups_internal(Port *port, int type)
+{
+	if (port->ssl)
+	{
+		int i, ngroups, *groups, nid;
+		StringInfoData str;
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			ngroups = SSL_get1_groups(port->ssl, NULL);
+		else
+			ngroups = SSL_get_shared_group(port->ssl, -1);
+
+		if (ngroups <= 0)
+			return NULL;
+
+		groups = palloc(ngroups * sizeof(*groups));
+		initStringInfo(&str);
+
+		if (type == SSL_SUPPORTED_GROUPS)
+			SSL_get1_groups(port->ssl, groups);
+
+		for (i = 0; i < ngroups; i++)
+		{
+			const char *name;
+
+			if (i)
+				appendStringInfo(&str, ":");
+
+			if (type == SSL_SUPPORTED_GROUPS)
+				nid = groups[i];
+			else
+				nid = SSL_get_shared_group(port->ssl, i);
+
+			name = SSL_group_to_name(port->ssl, nid);
+			appendStringInfoString(&str, ((name != NULL) ? name : "(null)"));
+		}
+
+		return str.data;
+	}
+	else
+		return NULL;
+}
+
+const char *
+be_tls_get_supported_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SUPPORTED_GROUPS);
+}
+
+const char *
+be_tls_get_shared_groups(Port *port)
+{
+	return be_tls_get_groups_internal(port, SSL_SHARED_GROUPS);
+}
+
 void
 be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len)
 {
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 921b2daa4ff..23e11fcb89d 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -317,6 +317,8 @@ extern ssize_t be_tls_write(Port *port, const void *ptr, size_t len, int *waitfo
 extern int	be_tls_get_cipher_bits(Port *port);
 extern const char *be_tls_get_version(Port *port);
 extern const char *be_tls_get_cipher(Port *port);
+extern const char *be_tls_get_supported_groups(Port *port);
+extern const char *be_tls_get_shared_groups(Port *port);
 extern void be_tls_get_peer_subject_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_issuer_name(Port *port, char *ptr, size_t len);
 extern void be_tls_get_peer_serial(Port *port, char *ptr, size_t len);

base-commit: 5b93a5987bd704d2363295eee919eee45f84c286
-- 
2.52.0


--ymr2ng6a3l6bfm2s--





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

* [PATCH] Reproduce filtering issue.
@ 2026-03-23 11:50 Antonin Houska <[email protected]>
  0 siblings, 0 replies; 285+ messages in thread

From: Antonin Houska @ 2026-03-23 11:50 UTC (permalink / raw)

---
 contrib/test_decoding/expected/filtering.out |  74 ++++++++++++++
 contrib/test_decoding/specs/filtering.spec   | 101 +++++++++++++++++++
 src/backend/executor/nodeModifyTable.c       |   2 +
 src/backend/replication/logical/snapbuild.c  |   3 +
 src/test/isolation/isolationtester.c         |   9 +-
 5 files changed, 188 insertions(+), 1 deletion(-)
 create mode 100644 contrib/test_decoding/expected/filtering.out
 create mode 100644 contrib/test_decoding/specs/filtering.spec

diff --git a/contrib/test_decoding/expected/filtering.out b/contrib/test_decoding/expected/filtering.out
new file mode 100644
index 00000000000..6ba9690509f
--- /dev/null
+++ b/contrib/test_decoding/expected/filtering.out
@@ -0,0 +1,74 @@
+Parsed test spec with 5 sessions
+
+starting permutation: s1_assign_xid s3_repack s2_assign_xid s1_rollback s4_insert s5_wakeup_snapbuild s2_rollback s5_wakeup_insert_speculative s4_insert_commit s5_wakeup_repack
+injection_points_attach
+-----------------------
+                       
+(1 row)
+
+injection_points_attach
+-----------------------
+                       
+(1 row)
+
+step s1_assign_xid: 
+    BEGIN;
+    CREATE TABLE c(i int);
+
+step s3_repack: 
+	REPACK (CONCURRENTLY) a;
+ <waiting ...>
+step s2_assign_xid: 
+    BEGIN;
+    CREATE TABLE d(i int);
+
+step s1_rollback: 
+    ROLLBACK;
+
+step s4_insert: 
+	BEGIN;
+	INSERT INTO t(i)
+	SELECT max(i) + 1 FROM t ON CONFLICT (i) DO UPDATE SET i=EXCLUDED.i;
+ <waiting ...>
+step s5_wakeup_snapbuild: 
+	SELECT injection_points_wakeup('snapbuild-full');
+
+injection_points_wakeup
+-----------------------
+                       
+(1 row)
+
+step s2_rollback: 
+    ROLLBACK;
+
+step s5_wakeup_insert_speculative: 
+	SELECT injection_points_wakeup('insert-speculative-before-confirm');
+
+injection_points_wakeup
+-----------------------
+                       
+(1 row)
+
+step s4_insert: <... completed>
+step s4_insert_commit: 
+	COMMIT;
+
+step s5_wakeup_repack: 
+	SELECT injection_points_wakeup('repack-concurrently-before-lock');
+
+injection_points_wakeup
+-----------------------
+                       
+(1 row)
+
+step s3_repack: <... completed>
+injection_points_detach
+-----------------------
+                       
+(1 row)
+
+injection_points_detach
+-----------------------
+                       
+(1 row)
+
diff --git a/contrib/test_decoding/specs/filtering.spec b/contrib/test_decoding/specs/filtering.spec
new file mode 100644
index 00000000000..a02c9f8facb
--- /dev/null
+++ b/contrib/test_decoding/specs/filtering.spec
@@ -0,0 +1,101 @@
+setup
+{
+	CREATE TABLE a(i int primary key, j int) WITH (autovacuum_enabled = off);
+	INSERT INTO a(i, j) VALUES (1, 1), (2, 2);
+	CREATE TABLE t(i int primary key);
+	INSERT INTO t(i) VALUES (1);
+	CREATE EXTENSION injection_points;
+}
+
+session s1
+step s1_assign_xid
+{
+    BEGIN;
+    CREATE TABLE c(i int);
+}
+step s1_rollback
+{
+    ROLLBACK;
+}
+
+session s2
+step s2_assign_xid
+{
+    BEGIN;
+    CREATE TABLE d(i int);
+}
+step s2_rollback
+{
+    ROLLBACK;
+}
+
+session s3
+setup
+{
+	SELECT injection_points_attach('snapbuild-full', 'wait');
+	SELECT injection_points_attach('repack-concurrently-before-lock', 'wait');
+}
+step s3_repack
+{
+	REPACK (CONCURRENTLY) a;
+}
+teardown
+{
+	SELECT injection_points_detach('repack-concurrently-before-lock');
+	SELECT injection_points_detach('snapbuild-full');
+}
+
+session s4
+setup
+{
+	SELECT injection_points_set_local();
+	SELECT injection_points_attach('insert-speculative-before-confirm', 'wait');
+}
+step s4_insert
+{
+	BEGIN;
+	INSERT INTO t(i)
+	SELECT max(i) + 1 FROM t ON CONFLICT (i) DO UPDATE SET i=EXCLUDED.i;
+}
+step s4_insert_commit
+{
+	COMMIT;
+}
+teardown
+{
+	SELECT injection_points_detach('insert-speculative-before-confirm');
+}
+
+session s5
+step s5_wakeup_snapbuild
+{
+	SELECT injection_points_wakeup('snapbuild-full');
+}
+step s5_wakeup_insert_speculative
+{
+	SELECT injection_points_wakeup('insert-speculative-before-confirm');
+}
+step s5_wakeup_repack
+{
+	SELECT injection_points_wakeup('repack-concurrently-before-lock');
+}
+
+permutation
+# Bring the snapshot builder to the FULL_SNAPSHOT state.
+s1_assign_xid
+s3_repack
+s2_assign_xid
+s1_rollback
+# Perform the speculative insert, but no confirmation so far. The snapshot
+# builder should decode it.
+s4_insert
+# Let the snapshout builder achieve CONSISTENT state and finish the setup.
+s5_wakeup_snapbuild
+s2_rollback
+# While REPACK is waiting on repack-concurrently-before-lock, let the insert
+# get confirmed. Relation filtering is now enabled.
+s5_wakeup_insert_speculative
+s4_insert_commit
+# REPACK should now decode the speculative insert and decode the speculative
+# insert (with the confirmation record filtered out).
+s5_wakeup_repack
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 680c29f35d5..6d5482e5746 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1232,6 +1232,8 @@ ExecInsert(ModifyTableContext *context,
 												   slot, arbiterIndexes,
 												   &specConflict);
 
+			INJECTION_POINT("insert-speculative-before-confirm", NULL);
+
 			/* adjust the tuple's state accordingly */
 			table_tuple_complete_speculative(resultRelationDesc, slot,
 											 specToken, !specConflict);
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index fbdd4600a2b..883e5b6e18f 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -141,6 +141,7 @@
 #include "storage/procarray.h"
 #include "storage/standby.h"
 #include "utils/builtins.h"
+#include "utils/injection_point.h"
 #include "utils/memutils.h"
 #include "utils/snapmgr.h"
 #include "utils/snapshot.h"
@@ -1390,6 +1391,8 @@ SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *runn
 		builder->state = SNAPBUILD_FULL_SNAPSHOT;
 		builder->next_phase_at = running->nextXid;
 
+		INJECTION_POINT("snapbuild-full", NULL);
+
 		ereport(LOG,
 				errmsg("logical decoding found initial consistent point at %X/%08X",
 					   LSN_FORMAT_ARGS(lsn)),
diff --git a/src/test/isolation/isolationtester.c b/src/test/isolation/isolationtester.c
index 440c875b8ac..8f17ee412c9 100644
--- a/src/test/isolation/isolationtester.c
+++ b/src/test/isolation/isolationtester.c
@@ -216,15 +216,22 @@ main(int argc, char **argv)
 	 * exactly expect concurrent use of test tables.  However, autovacuum will
 	 * occasionally take AccessExclusiveLock to truncate a table, and we must
 	 * ignore that transient wait.
+	 *
+	 * If the session's backend is blocked, and if its background worker is
+	 * waiting on an injection point, we assume that the injection point is
+	 * the reason for the backend to be blocked. That's what we check in the
+	 * second query of the UNION. XXX Should we use a separate query for that?
 	 */
 	initPQExpBuffer(&wait_query);
 	appendPQExpBufferStr(&wait_query,
+						 "WITH blocking(res) AS ("
 						 "SELECT pg_catalog.pg_isolation_test_session_is_blocked($1, '{");
 	/* The spec syntax requires at least one session; assume that here. */
 	appendPQExpBufferStr(&wait_query, conns[1].backend_pid_str);
 	for (i = 2; i < nconns; i++)
 		appendPQExpBuffer(&wait_query, ",%s", conns[i].backend_pid_str);
-	appendPQExpBufferStr(&wait_query, "}')");
+	appendPQExpBufferStr(&wait_query, "}') UNION "
+						 "SELECT pg_catalog.pg_isolation_test_session_is_blocked(pid, '{}') FROM pg_stat_activity WHERE leader_pid=$1) SELECT bool_or(res) FROM blocking");
 
 	res = PQprepare(conns[0].conn, PREP_WAITING, wait_query.data, 0, NULL);
 	if (PQresultStatus(res) != PGRES_COMMAND_OK)
-- 
2.47.3


--=-=-=--





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


end of thread, other threads:[~2026-03-23 11:50 UTC | newest]

Thread overview: 285+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-02-19 15:33 [PATCH v1] contrib/sslinfo: Add ssl_(supported|shared)_groups Dmitrii Dolgov <[email protected]>
2026-03-23 11:50 [PATCH] Reproduce filtering issue. Antonin Houska <[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